HTTP request

Nette zapouzdřuje HTTP požadavek do objektů se srozumitelným API a zároveň poskytuje sanitizační filtr.

HTTP požadavek představuje objekt Nette\Http\Request. Pokud pracujete s Nette, tento objekt je automaticky vytvořen frameworkem a můžete si jej nechat předat pomocí dependency injection. V presenterech stačí jen zavolat metodu $this->getHttpRequest(). Pokud pracujete mimo Nette Framework, můžete si vytvořit objekt pomocí RequestFactory.

Velkou předností Nette je, že při vytváření objektu automaticky pročišťuje všechny vstupní parametry GET, POST, COOKIE a také URL od kontrolních znaků a neplatných UTF-8 sekvencí. S těmito daty pak můžete bezpečně dále pracovat. Očištěná data se následně používají v presenterech a formulářích.

Instalace a požadavky

Nette\Http\Request

Tento objekt je immutable (neměnný). Nemá žádné settery, má jen jeden tzv. wither withUrl(), který objekt nemění, ale vrací novou instanci se změněnou hodnotou.

withUrl (Nette\Http\UrlScript $url): Nette\Http\Request

Vrací klon s jinou URL.

getUrl(): Nette\Http\UrlScript

Vrací URL požadavku jako objekt UrlScript.

$url = $httpRequest->getUrl();
echo $url; // https://doc.nette.org/cs/?action=edit
echo $url->getHost(); // nette.org

Upozornění: prohlížeče neodesílají na server fragment, takže $url->getFragment() bude vracet prázdný řetězec.

getQuery (?string $key=null): string|array|null

Vrací parametry GET požadavku.

$all = $httpRequest->getQuery(); // vrací pole všech parametrů z URL
$id = $httpRequest->getQuery('id'); // vrací GET parametr 'id' (nebo null)

getPost (?string $key=null): string|array|null

Vrací parametry POST požadavku.

$all = $httpRequest->getPost(); // vrací pole všech parametrů z POST
$id = $httpRequest->getPost('id'); // vrací POST parametr 'id' (nebo null)

getFile (string|string[] $key): Nette\Http\FileUpload|array|null

Vrací upload jako objekt Nette\Http\FileUpload:

$file = $httpRequest->getFile('avatar');
if ($file?->hasFile()) { // byl nějaký soubor nahraný?
	$file->getUntrustedName(); // jméno souboru odeslané uživatelem
	$file->getSanitizedName(); // jméno bez nebezpečných znaků
}

Pro přístup do zanořené struktury uveďte pole klíčů.

//<input type="file" name="my-form[details][avatar]" multiple>
$file = $request->getFile(['my-form', 'details', 'avatar']);

Protože nelze důvěřovat datům zvenčí a tedy ani spoléhat na podobu struktury souborů, je bezpečnější tento způsob než třeba $request->getFiles()['my-form']['details']['avatar'], který může selhat.

getFiles(): array

Vrátí strom všech uploadů v normalizované struktuře, jejíž listy jsou objekty Nette\Http\FileUpload:

$files = $httpRequest->getFiles();

getCookie (string $key): string|array|null

Vrací cookie nebo null, když neexistuje.

$sessId = $httpRequest->getCookie('sess_id');

getCookies(): array

Vrací všechny cookies.

$cookies = $httpRequest->getCookies();

getMethod(): string

Vrací HTTP metodu, se kterou byl udělán požadavek.

$httpRequest->getMethod(); // GET, POST, HEAD, PUT

isMethod (string $method)bool

Testuje HTTP metodu, se kterou byl udělán požadavek. Parametr je case-insensitive.

if ($httpRequest->isMethod('GET')) // ...

getHeader (string $header): ?string

Vrací HTTP hlavičku nebo null, pokud neexistuje. Parametr je case-insensitive.

$userAgent = $httpRequest->getHeader('User-Agent');

getHeaders(): array

Vrací všechny HTTP hlavičky jako asociativní pole.

$headers = $httpRequest->getHeaders();
echo $headers['Content-Type'];

isSecured(): bool

Je spojení šifrované (HTTPS)? Pro správnou funkčnost může být potřeba nastavit proxy.

isSameSite(): bool

Přišel požadavek ze stejné stránky (same-site)? Od verze 3.4 ji nahrazuje schopnější metoda isFrom().

isFrom (FetchSite|array $site, FetchDest|array|null $dest=null, ?bool $user=null)bool

Řekne vám, odkud požadavek přišel a jak ho prohlížeč vytvořil, na základě hlaviček Sec-Fetch-* (tzv. Fetch Metadata), které prohlížeč nastavuje sám a stránka běžící v prohlížeči oběti je nedokáže zfalšovat ani odstranit. Nette ji interně používá k automatické ochraně formulářů a signálů před útoky Cross-Site Request Forgery (CSRF). Hodí se, když chcete ochránit vlastní citlivé akce, jako jsou API endpointy nebo destruktivní odkazy.

Metoda vrátí true pouze tehdy, když požadavek splňuje všechny zadané podmínky. První parametr $site popisuje vztah mezi stránkou, která požadavek vyvolala, a vaším webem (hlavička Sec-Fetch-Site). Přijímá jednu hodnotu nebo pole těchto hodnot výčtu FetchSite:

  • FetchSite::SameOrigin – ze zcela stejného původu (schéma, host i port)
  • FetchSite::SameSite – ze stejného webu, případně z jiné subdomény
  • FetchSite::CrossSite – z cizího webu
  • FetchSite::None – uživatel jej vyvolal přímo, např. zadáním URL nebo otevřením záložky
// přišel požadavek z našich vlastních stránek?
if (!$httpRequest->isFrom([FetchSite::SameOrigin, FetchSite::SameSite])) {
	// akci zablokujeme
}

Volitelný parametr $dest (hlavička Sec-Fetch-Dest) udává, jaký druh zdroje prohlížeč načítá, např. FetchDest::Document pro navigaci na stránku nebo FetchDest::Empty pro požadavek z JavaScriptu. Volitelný parametr $user (hlavička Sec-Fetch-User) říká, zda navigaci vyvolala skutečná akce uživatele, jako kliknutí na odkaz či odeslání formuláře; hodnotou true ji vyžadujete.

Kontrola, že je akce dostupná pouze z vlastních stránek a jen skutečnou akcí uživatele, pak vypadá takto:

if (!$httpRequest->isFrom(FetchSite::SameOrigin, FetchDest::Document, user: true)) {
	$this->error();
}

Starší prohlížeče (Safari před 16.4) hlavičky Sec-Fetch-* neposílají. Pro ně Nette použije záložně cookie SameSite=Strict, která dokazuje pouze to, že požadavek není cross-site. Kontrolu, která navíc vyžaduje $dest nebo $user, tak nelze tímto způsobem ověřit a v těchto prohlížečích vrátí false – pokud je to příliš striktní, testujte jen $site.

isAjax(): bool

Jde o AJAXový požadavek?

getRemoteAddress(): ?string

Vrací IP adresu uživatele. Pro správnou funkčnost může být potřeba nastavit proxy.

getRemoteHost(): ?string

Vrací DNS překlad IP adresy uživatele. Pro správnou funkčnost může být potřeba nastavit proxy.

getBasicCredentials(): ?array

Vrací ověřovací údaje pro Basic HTTP authentication.

[$user, $password] = $httpRequest->getBasicCredentials();

getRawBody(): ?string

Vrací tělo HTTP požadavku.

$body = $httpRequest->getRawBody();

getOrigin(): ?UrlImmutable

Vrací origin, ze kterého požadavek přišel. Origin se skládá z protokolu, hostname a portu – například https://example.com:8080. Vrací null, pokud hlavička origin není přítomna nebo je nastavena na 'null'.

$origin = $httpRequest->getOrigin();
echo $origin; // https://example.com:8080
echo $origin?->getHost(); // example.com

Prohlížeč posílá hlavičku Origin v následujících případech:

  • Požadavky mezi doménami (AJAX volání na jinou doménu)
  • POST, PUT, DELETE a další modifikující požadavky
  • Požadavky provedené pomocí Fetch API

Prohlížeč NEPOSÍLÁ hlavičku Origin při:

  • Běžných GET požadavcích na stejnou doménu (navigace v rámci téže domény)
  • Přímé navigaci zadáním URL do adresního řádku
  • Požadavcích z jiných klientů než prohlížeče (pokud není ručně přidána)

Na rozdíl od hlavičky Referer obsahuje Origin pouze schéma, host a port – nikoli celou cestu URL. To ji činí vhodnější pro bezpečnostní kontroly při zachování soukromí uživatele. Hlavička Origin se primárně používá pro validaci CORS (Cross-Origin Resource Sharing).

detectLanguage (array $langs): ?string

Detekuje jazyk. Jako parametr $lang předáme pole s jazyky, které aplikace podporuje, a ona vrátí ten, který by viděl návštěvníkův prohlížeč nejraději. Nejsou to žádná kouzla, jen se využívá hlavičky Accept-Language. Pokud nedojde k žádné shodě, vrací null.

// prohlížeč odesílá např. Accept-Language: cs,en-us;q=0.8,en;q=0.5,sl;q=0.3

$langs = ['hu', 'pl', 'en']; // jazyky podporované aplikací
echo $httpRequest->detectLanguage($langs); // en

RequestFactory

Třída Nette\Http\RequestFactory slouží k vytvoření instance Nette\Http\Request, která reprezentuje aktuální HTTP požadavek. (Pokud pracujete s Nette, objekt HTTP požadavku je automaticky vytvořen frameworkem.)

$factory = new Nette\Http\RequestFactory;
$httpRequest = $factory->fromGlobals();

Metoda fromGlobals() vytvoří objekt požadavku na základě aktuálních globálních proměnných PHP ($_GET, $_POST, $_COOKIE, $_FILES a $_SERVER). Při vytváření objektu automaticky pročišťuje všechny vstupní parametry GET, POST, COOKIE a také URL od kontrolních znaků a neplatných UTF-8 sekvencí, což zajišťuje bezpečnost při další práci s těmito daty.

RequestFactory lze před zavoláním fromGlobals() konfigurovat:

  • metodou $factory->setBinary() vypnete automatické čištění vstupních parametrů od kontrolních znaků a neplatných UTF-8 sekvencí.
  • metodou $factory->setProxy(...) uvedete IP adresu proxy serveru, což je nezbytné pro správnou detekci IP adresy uživatele.
  • metodou $factory->setForceHttps() .{data-version:3.3.4} vynutíte HTTPS schéma požadavku bez ohledu na prostředí serveru.

RequestFactory umožňuje definovat filtry, které automaticky transformují části URL požadavku. Tyto filtry odstraňují nežádoucí znaky z URL, které tam mohou být vloženy například nesprávnou implementací komentářových systémů na různých webech:

// odstranění mezer z cesty
$requestFactory->urlFilters['path']['%20'] = '';

// odstranění tečky, čárky nebo pravé závorky z konce URI
$requestFactory->urlFilters['url']['[.,)]$'] = '';

// vyčištění cesty od zdvojených lomítek (výchozí filtr)
$requestFactory->urlFilters['path']['/{2,}'] = '/';

První klíč 'path' nebo 'url' určuje, na kterou část URL se filtr použije. Druhý klíč je regulární výraz, který se má vyhledat, a hodnota je náhrada, která se použije místo nalezeného textu.

Uploadované soubory

Metoda Nette\Http\Request::getFiles() vrací pole všech uploadů v normalizované struktuře, jejíž listy jsou objekty Nette\Http\FileUpload. Ty zapouzdřují data odeslaná formulářovým prvkem <input type=file>.

Struktura reflektuje pojmenování prvků v HTML. V nejjednodušším případě to může být jediný pojmenovaný element formuláře odeslaný jako:

<input type="file" name="avatar">

V tomto případě $request->getFiles() vrací pole:

[
	'avatar' => /* FileUpload instance */
]

Objekt FileUpload se vytvoří i v případě, že uživatel žádný soubor neodeslal nebo odeslání selhalo. Jestli byl soubor odeslán vrací metoda hasFile():

$request->getFile('avatar')?->hasFile();

V případě názvu elementu používajícího notaci pro pole:

<input type="file" name="my-form[details][avatar]">

vypadá vrácený strom takto:

[
	'my-form' => [
		'details' => [
			'avatar' => /* FileUpload instance */
		],
	],
]

Lze vytvořit i pole souborů:

<input type="file" name="my-form[details][avatars][]" multiple>

V takovém případě vypadá struktura takto:

[
	'my-form' => [
		'details' => [
			'avatars' => [
				0 => /* FileUpload instance */,
				1 => /* FileUpload instance */,
				2 => /* FileUpload instance */,
			],
		],
	],
]

Přistoupit k indexu 1 vnořeného pole lze nejlépe takto:

$file = $request->getFile(['my-form', 'details', 'avatars', 1]);
if ($file instanceof FileUpload) {
	// ...
}

Protože nelze důvěřovat datům zvenčí a tedy ani spoléhat na podobu struktury souborů, je bezpečnější tento způsob než třeba $request->getFiles()['my-form']['details']['avatars'][1], který může selhat.

Přehled metod FileUpload

hasFile(): bool

Vrací true, pokud uživatel nějaký soubor uploadoval.

isOk(): bool

Vrací true, pokud soubor byl nahrán úspěšně.

getError(): int

Vrací kód chyby při uploadu souboru. Jde o jednu z konstant UPLOAD_ERR_XXX. V případě, že upload proběhl v pořádku, vrací UPLOAD_ERR_OK.

move (string $dest)

Přesune nahraný soubor do nového umístění. Pokud cílový soubor již existuje, bude přepsán.

$file->move('/path/to/files/name.ext');

getContents(): ?string

Vrací obsah uploadovaného souboru. V případě, že upload nebyl úspěšný, vrací null.

getContentType(): ?string

Detekuje MIME content type uploadovaného souboru na základě jeho signatury. V případě, že upload nebyl úspěšný nebo detekce se nezdařila, vrací null.

Vyžaduje PHP rozšíření fileinfo.

getUntrustedName(): string

Vrací originální název souboru, jak jej odeslal prohlížeč.

Nevěřte hodnotě vrácené touto metodou. Klient mohl odeslat škodlivý název souboru s úmyslem poškodit nebo hacknout vaši aplikaci.

getSanitizedName(): string

Vrací sanitizovaný název souboru. Obsahuje pouze ASCII znaky [a-zA-Z0-9.-]. Pokud název takové znaky neobsahuje, vrátí 'unknown'. Pokud je soubor obrázek ve formátu JPEG, PNG, GIF, WebP nebo AVIF, vrátí i správnou příponu.

Vyžaduje PHP rozšíření fileinfo.

getSuggestedExtension(): ?string

Vrací vhodnou příponu souboru (bez tečky) odpovídající zjištěnému MIME typu.

Vyžaduje PHP rozšíření fileinfo.

getUntrustedFullPath(): string

Vrací originální cestu k souboru, jak ji odeslal prohlížeč při uploadu složky. Celá cesta je dostupná pouze v PHP 8.1 a vyšším. V předchozích verzích tato metoda vrací originální název souboru.

Nevěřte hodnotě vrácené touto metodou. Klient mohl odeslat škodlivý název souboru s úmyslem poškodit nebo hacknout vaši aplikaci.

getSize(): int

Vrací velikost uploadovaného souboru. V případě, že upload nebyl úspěšný, vrací 0.

getTemporaryFile(): string

Vrací cestu k dočasné lokaci uploadovaného souboru. V případě, že upload nebyl úspěšný, vrací ''.

__toString(): string

Vrací cestu k dočasnému umístění nahraného souboru. To umožňuje objekt FileUpload použít přímo jako řetězec.

isImage(): bool

Vrací true, pokud nahraný soubor je obrázek ve formátu JPEG, PNG, GIF, WebP nebo AVIF. Detekce probíhá na základě jeho signatury a neověřuje se integrita celého souboru. Zda není obrázek poškozený lze zjistit například pokusem o jeho načtení.

Vyžaduje PHP rozšíření fileinfo.

getImageSize(): ?array

Vrací dvojici [šířka, výška] s rozměry uploadovaného obrázku. V případě, že upload nebyl úspěšný nebo nejde o platný obrázek, vrací null.

toImage(): Nette\Utils\Image

Načte obrázek jak objekt Image. V případě, že upload nebyl úspěšný nebo nejde o platný obrázek, vyhodí výjimku Nette\Utils\ImageException.

verze: 4.0 3.x 2.x