Żądanie HTTP
Nette zamyka żądanie HTTP w obiektach o przejrzystym API, a przy okazji udostępnia filtr oczyszczający.
Żądanie HTTP reprezentuje obiekt Nette\Http\Request.
Jeśli pracujesz z Nette, obiekt ten tworzy framework automatycznie, a możesz sobie go pozwolić przekazać przez wstrzykiwanie zależności. W presenterach wystarczy
wywołać metodę $this->getHttpRequest(). Jeśli pracujesz poza Nette Framework, możesz utworzyć obiekt za
pomocą RequestFactory.
Ogromną zaletą Nette jest to, że przy tworzeniu obiektu automatycznie oczyszcza wszystkie parametry wejściowe (GET, POST, COOKIE) oraz URL ze znaków sterujących i nieprawidłowych sekwencji UTF-8. Z tymi danymi możesz potem bezpiecznie pracować. Oczyszczone dane są następnie używane w presenterach i formularzach.
Nette\Http\Request
Obiekt ten jest niezmienny. Nie ma setterów, ma tylko jeden tak zwany wither, withUrl(), który nie zmienia
obiektu, tylko zwraca nową instancję ze zmienioną wartością.
withUrl (Nette\Http\UrlScript $url): Nette\Http\Request
Zwraca klon z innym URL.
getUrl(): Nette\Http\UrlScript
Zwraca URL żądania jako obiekt UrlScript.
$url = $httpRequest->getUrl();
echo $url; // https://nette.org/en/documentation?action=edit
echo $url->getHost(); // nette.org
Uwaga: przeglądarki nie wysyłają fragmentu na serwer, więc $url->getFragment() zwróci pusty ciąg.
getQuery (?string $key=null): string|array|null
Zwraca parametry żądania GET.
$all = $httpRequest->getQuery(); // tablica wszystkich parametrów URL
$id = $httpRequest->getQuery('id'); // zwraca parametr GET 'id' (albo null)
getPost (?string $key=null): string|array|null
Zwraca parametry żądania POST.
$all = $httpRequest->getPost(); // tablica wszystkich parametrów POST
$id = $httpRequest->getPost('id'); // zwraca parametr POST 'id' (albo null)
getFile (string|string[] $key): ?Nette\Http\FileUpload
Zwraca upload jako obiekt Nette\Http\FileUpload:
$file = $httpRequest->getFile('avatar');
if ($file?->hasFile()) { // czy jakiś plik został wysłany?
$file->getUntrustedName(); // nazwa pliku wysłana przez użytkownika
$file->getSanitizedName(); // nazwa bez niebezpiecznych znaków
}
Żeby dostać się do struktury zagnieżdżonej, podaj tablicę kluczy.
// <input type="file" name="my-form[details][avatar]">
$file = $request->getFile(['my-form', 'details', 'avatar']);
Ponieważ danym zewnętrznym nie można ufać i tym samym polegać na strukturze plików, to podejście jest bezpieczniejsze
niż na przykład $request->getFiles()['my-form']['details']['avatar'], które mogłoby zawieść.
getFiles(): array
Zwraca drzewo wszystkich uploadów w znormalizowanej strukturze, której liśćmi są obiekty Nette\Http\FileUpload:
$files = $httpRequest->getFiles();
getCookie (string $key): ?string
Zwraca cookie albo null, jeśli nie istnieje.
$sessId = $httpRequest->getCookie('sess_id');
getCookies(): array
Zwraca wszystkie cookies.
$cookies = $httpRequest->getCookies();
getMethod(): string
Zwraca metodę HTTP użytą w żądaniu.
$httpRequest->getMethod(); // GET, POST, HEAD, PUT
isMethod (string $method): bool
Testuje metodę HTTP użytą w żądaniu. Parametr nie rozróżnia wielkości liter.
if ($httpRequest->isMethod('GET')) // ...
getHeader (string $header): ?string
Zwraca nagłówek HTTP albo null, jeśli nie istnieje. Parametr nie rozróżnia wielkości liter.
$userAgent = $httpRequest->getHeader('User-Agent');
getHeaders(): array<string, string>
Zwraca wszystkie nagłówki HTTP jako tablicę asocjacyjną. Klucze są znormalizowane do małych liter.
$headers = $httpRequest->getHeaders();
echo $headers['content-type'];
isSecured(): bool
Czy połączenie jest szyfrowane (HTTPS)? Poprawne działanie może wymagać ustawienia proxy.
isSameSite(): bool
Czy żądanie przyszło z tej samej witryny? Od wersji 3.4 zastępuje je bardziej wszechstronne isFrom().
isFrom (FetchSite|array $site, FetchDest|array|null $dest=null, ?bool $user=null): bool
Mówi Ci, skąd żądanie przyszło i w jaki sposób przeglądarka je wykonała, na podstawie nagłówków
Sec-Fetch-* (tak zwane Fetch Metadata), które przeglądarka
ustawia sama i których strona działająca w przeglądarce ofiary nie może ani podrobić, ani usunąć. Nette używa tego
wewnętrznie do automatycznej ochrony formularzy i sygnałów przed Cross-Site Request Forgery (CSRF). Przydaje się,
gdy chcesz zabezpieczyć własne wrażliwe akcje, na przykład endpointy API albo destrukcyjne odnośniki.
Metoda zwraca true tylko wtedy, gdy żądanie spełnia wszystkie podane przez Ciebie warunki. Pierwszy
parametr $site opisuje relację między stroną, która zainicjowała żądanie, a Twoją witryną (nagłówek
Sec-Fetch-Site). Przyjmuje pojedynczą wartość albo listę tych przypadków FetchSite:
FetchSite::SameOrigin– z dokładnie tego samego origin (schemat, host i port)FetchSite::SameSite– z tej samej witryny, ewentualnie z innej subdomenyFetchSite::CrossSite– z obcej witrynyFetchSite::None– użytkownik zainicjował je bezpośrednio, np. wpisując URL albo otwierając zakładkę
// czy żądanie pochodzi z naszych własnych stron?
if (!$httpRequest->isFrom([FetchSite::SameOrigin, FetchSite::SameSite])) {
// zablokuj akcję
}
Opcjonalny parametr $dest (nagłówek Sec-Fetch-Dest) mówi, jakiego rodzaju zasób przeglądarka
pobiera, np. FetchDest::Document dla nawigacji najwyższego poziomu albo FetchDest::Empty dla żądania
wykonanego z JavaScriptu. Opcjonalny parametr $user (nagłówek Sec-Fetch-User) wskazuje, czy nawigacja
została wywołana prawdziwą akcją użytkownika, jak kliknięcie odnośnika albo wysłanie formularza; przekaż
true, żeby tego wymagać.
Kontrola, że akcja jest osiągalna tylko z Twoich własnych stron i tylko przez prawdziwą akcję użytkownika, wygląda wtedy tak:
if (!$httpRequest->isFrom(FetchSite::SameOrigin, FetchDest::Document, user: true)) {
$this->error();
}
Starsze przeglądarki (Safari przed 16.4) nie wysyłają nagłówków Sec-Fetch-*. Dla nich Nette
wraca do cookie SameSite=Strict, które dowodzi tylko tego, że żądanie nie jest cross-site. Kontroli, która
dodatkowo wymaga $dest albo $user, nie da się w ten sposób zweryfikować i w tych przeglądarkach
zwraca false; jeśli to zbyt surowe, testuj tylko $site.
isAjax(): bool
Czy to żądanie AJAX?
getRemoteAddress(): ?string
Zwraca adres IP użytkownika. Poprawne działanie może wymagać ustawienia proxy.
getRemoteHost(): ?string
Przestarzała, zawsze zwraca null. Odwrotne zapytania DNS były wolne i zawodne; jeśli potrzebujesz nazwy hosta,
rozwiąż ją sam z getRemoteAddress().
getBasicCredentials(): ?array
Zwraca dane uwierzytelniające dla Basic HTTP authentication.
[$user, $password] = $httpRequest->getBasicCredentials();
getRawBody(): ?string
Zwraca ciało żądania HTTP.
$body = $httpRequest->getRawBody();
getOrigin(): ?UrlImmutable
Zwraca origin, z którego przyszło żądanie. Origin składa się ze schematu (protokołu), nazwy hosta i portu, na
przykład https://example.com:8080. Zwraca null, jeśli nagłówek origin nie jest obecny albo ma
wartość 'null'.
$origin = $httpRequest->getOrigin();
echo $origin; // https://example.com:8080
echo $origin?->getHost(); // example.com
Przeglądarka wysyła nagłówek Origin w tych przypadkach:
- żądania cross-origin (wywołania AJAX do innej domeny)
- POST, PUT, DELETE i inne żądania modyfikujące
- żądania wykonane przez Fetch API
Przeglądarka NIE wysyła nagłówka Origin dla:
- zwykłych żądań GET do tej samej domeny (nawigacja same-origin)
- bezpośredniej nawigacji przez wpisanie URL w pasku adresu
- żądań od klientów niebędących przeglądarką
W przeciwieństwie do nagłówka Referer Origin zawiera tylko schemat, host i port, a
nie pełną ścieżkę URL. Czyni to go odpowiedniejszym do kontroli bezpieczeństwa przy zachowaniu prywatności użytkownika.
Nagłówek Origin używany jest przede wszystkim do walidacji CORS (Cross-Origin Resource Sharing).
detectLanguage (array $langs): ?string
Wykrywa język. Jako parametr $langs przekaż tablicę języków obsługiwanych przez aplikację, a metoda zwróci
ten, który preferuje przeglądarka odwiedzającego. Nie ma w tym magii, wykorzystywany jest po prostu nagłówek
Accept-Language. Jeśli nie znajdzie dopasowania, zwraca null.
// Przeglądarka wysyła np. Accept-Language: cs,en-us;q=0.8,en;q=0.5,sl;q=0.3
$langs = ['hu', 'pl', 'en']; // języki obsługiwane przez aplikację
echo $httpRequest->detectLanguage($langs); // en
RequestFactory
Klasa Nette\Http\RequestFactory służy do
utworzenia instancji Nette\Http\Request, która reprezentuje bieżące żądanie HTTP. (Jeśli pracujesz z Nette,
obiekt żądania HTTP tworzy framework automatycznie.)
$factory = new Nette\Http\RequestFactory;
$httpRequest = $factory->fromGlobals();
Metoda fromGlobals() tworzy obiekt żądania na podstawie bieżących zmiennych globalnych PHP
($_GET, $_POST, $_COOKIE, $_FILES i $_SERVER). Przy tworzeniu
obiektu automatycznie oczyszcza wszystkie parametry wejściowe (GET, POST, COOKIE) oraz URL ze znaków sterujących
i nieprawidłowych sekwencji UTF-8, co zapewnia bezpieczeństwo przy późniejszej pracy z tymi danymi.
RequestFactory można skonfigurować przed wywołaniem fromGlobals():
- metoda
$factory->setBinary()wyłącza automatyczne oczyszczanie parametrów wejściowych ze znaków sterujących i nieprawidłowych sekwencji UTF-8. - metoda
$factory->setProxy(...)podaje adres IP serwera proxy, co jest potrzebne do poprawnego wykrywania adresu IP użytkownika. - metoda
$factory->setForceHttps().{data-version:3.3.4} wymusza schemat żądania HTTPS niezależnie od środowiska serwera.
RequestFactory pozwala definiować filtry automatycznie przekształcające części URL żądania. Filtry te usuwają z URL-i niepożądane znaki, które mogły zostać do nich wstawione na przykład przez niepoprawne implementacje systemów komentarzy na różnych stronach:
// usuwa spacje ze ścieżki
$requestFactory->urlFilters['path']['%20'] = '';
// usuwa kropkę, przecinek albo prawy nawias z końca URI
$requestFactory->urlFilters['url']['[.,)]$'] = '';
// czyści ścieżkę z podwójnych ukośników (filtr domyślny)
$requestFactory->urlFilters['path']['/{2,}'] = '/';
Pierwszy klucz, 'path' albo 'url', określa, do której części URL filtr zostanie zastosowany.
Drugi klucz to wyrażenie regularne do wyszukania, a wartość to zamiennik, który ma zostać użyty zamiast znalezionego
tekstu.
Wysłane pliki
Metoda Nette\Http\Request::getFiles() zwraca tablicę wszystkich uploadów w znormalizowanej strukturze, której
liśćmi są obiekty Nette\Http\FileUpload. Zamykają
one w sobie dane wysłane elementem formularza <input type=file>.
Struktura odzwierciedla nazewnictwo elementów w HTML. W najprostszym przypadku może to być pojedynczy nazwany element formularza wysłany jako:
<input type="file" name="avatar">
W takim przypadku $request->getFiles() zwraca tablicę:
[
'avatar' => /* instancja FileUpload */
]
Obiekt FileUpload powstaje także wtedy, gdy użytkownik żadnego pliku nie wysłał albo wysyłanie się nie
powiodło. To, czy plik został wysłany, zwraca metoda hasFile():
$request->getFile('avatar')?->hasFile();
W przypadku nazwy elementu z zapisem tablicowym:
<input type="file" name="my-form[details][avatar]">
zwrócone drzewo wygląda tak:
[
'my-form' => [
'details' => [
'avatar' => /* instancja FileUpload */
],
],
]
Możesz też tworzyć tablice plików:
<input type="file" name="my-form[details][avatars][]" multiple>
W takim przypadku struktura wygląda tak:
[
'my-form' => [
'details' => [
'avatars' => [
0 => /* instancja FileUpload */,
1 => /* instancja FileUpload */,
2 => /* instancja FileUpload */,
],
],
],
]
Do indeksu 1 zagnieżdżonej tablicy najlepiej dostać się tak:
$file = $request->getFile(['my-form', 'details', 'avatars', 1]);
if ($file instanceof Nette\Http\FileUpload) {
// ...
}
Ponieważ danym zewnętrznym nie można ufać i tym samym polegać na strukturze plików, to podejście jest bezpieczniejsze
niż na przykład $request->getFiles()['my-form']['details']['avatars'][1], które mogłoby zawieść.
Przegląd metod FileUpload
hasFile(): bool
Zwraca true, jeśli użytkownik wysłał plik.
isOk(): bool
Zwraca true, jeśli plik został wysłany pomyślnie.
getError(): int
Zwraca kod błędu związany z wysłanym plikiem. Jest to jedna ze stałych UPLOAD_ERR_XXX. Jeśli plik został wysłany pomyślnie,
zwraca UPLOAD_ERR_OK.
move (string $dest)
Przenosi wysłany plik w nowe miejsce. Jeśli plik docelowy już istnieje, zostanie nadpisany.
$file->move('/path/to/files/name.ext');
getContents(): ?string
Zwraca zawartość wysłanego pliku. Jeśli wysyłanie się nie powiodło, zwraca null.
getContentType(): ?string
Wykrywa typ zawartości MIME wysłanego pliku na podstawie jego sygnatury. Jeśli wysyłanie się nie powiodło albo wykrywanie
zawiodło, zwraca null.
Wymaga rozszerzenia PHP fileinfo.
getUntrustedName(): string
Zwraca oryginalną nazwę pliku wysłaną przez przeglądarkę.
Nie ufaj wartości zwracanej przez tę metodę. Klient mógł wysłać złośliwą nazwę pliku z zamiarem uszkodzenia albo zhakowania Twojej aplikacji.
getSanitizedName(): string
Zwraca oczyszczoną nazwę pliku. Zawiera tylko znaki ASCII [a-zA-Z0-9.-]. Jeśli nazwa takich znaków nie
zawiera, zwraca 'unknown'. Jeśli plik jest obrazkiem JPEG, PNG, GIF, WebP albo AVIF, zwraca też poprawne
rozszerzenie pliku.
Wymaga rozszerzenia PHP fileinfo.
getSuggestedExtension(): ?string
Zwraca odpowiednie rozszerzenie pliku (bez kropki) odpowiadające wykrytemu typowi MIME.
Wymaga rozszerzenia PHP fileinfo.
getUntrustedFullPath(): string
Zwraca oryginalną ścieżkę pliku wysłaną przez przeglądarkę przy wysyłaniu katalogu. Pełna ścieżka dostępna jest tylko w PHP 8.1 i nowszym. W poprzednich wersjach ta metoda zwraca oryginalną nazwę pliku.
Nie ufaj wartości zwracanej przez tę metodę. Klient mógł wysłać złośliwą nazwę pliku z zamiarem uszkodzenia albo zhakowania Twojej aplikacji.
getSize(): int
Zwraca rozmiar wysłanego pliku. Jeśli wysyłanie się nie powiodło, zwraca 0.
getTemporaryFile(): string
Zwraca ścieżkę do tymczasowej lokalizacji wysłanego pliku. Jeśli wysyłanie się nie powiodło, zwraca
''.
__toString(): string
Zwraca ścieżkę do tymczasowej lokalizacji wysłanego pliku. Pozwala to używać obiektu FileUpload
bezpośrednio jako ciągu.
isImage(): bool
Zwraca true, jeśli wysłany plik jest obrazkiem JPEG, PNG, GIF, WebP albo AVIF. Wykrywanie odbywa się na
podstawie jego sygnatury i nie weryfikuje integralności całego pliku. To, czy obrazek nie jest uszkodzony, można ustalić na
przykład, próbując go wczytać.
Wymaga rozszerzenia PHP fileinfo.
getImageSize(): ?array
Zwraca parę [width, height] z wymiarami wysłanego obrazka. Jeśli wysyłanie się nie powiodło albo nie jest
to poprawny obrazek, zwraca null.
toImage(): Nette\Utils\Image
Wczytuje obrazek jako obiekt Image. Jeśli wysyłanie się nie powiodło
albo nie jest to poprawny obrazek, rzuca Nette\Utils\ImageException.