Sessions
HTTP je bezestavový protokol, nicméně takřka každá aplikace potřebuje stav mezi požadavky uchovávat, například obsah nákupního košíku. Právě k tomu slouží session neboli relace. Ukážeme si,
- jak používat sessions
- jak předejít jmenným konfliktům
- jak nastavit expiraci
Při použití sessions každý uživatel obdrží jedinečný identifikátor nazývaný session ID, který se předává v cookie. Ten slouží jako klíč k session datům. Na rozdíl od cookies, které se uchovávají na straně prohlížeče, jsou data v session uchovávána na straně serveru.
Session nastavujeme v konfiguraci, důležitá je zejména volba doby exipirace.
Správu session má na starosti objekt Nette\Http\Session, ke kterému se dostanete tak, že si jej
necháte předat pomocí dependency
injection. V presenterech stačí jen zavolat $session = $this->getSession()
.
Start session
Nette ve výchozím nastavení automaticky zahájí session v případě, že HTTP požadavek obsahuje cookie se session ID.
Automaticky ji zahájí také v okamžiku, když z ní začneme číst nebo do ní zapisovat data. Ručně se session zahájí
pomocí $session->start()
.
PHP odešle při spuštění session HTTP hlavičky ovlivňující kešování, viz session_cache_limiter, a případně i cookie se session ID. Proto je nutné vždy session nastartovat ještě před odesláním jakéhokoliv výstupu do prohlížeče, jinak dojde k vyhození výjimky. Pokud tedy víte, že v průběhu vykreslování stránky se bude používat session, nastartujte ji ručně předtím, třeba v presenteru.
Ve vývojářském režimu startuje session Tracy, protože ji používá pro zobrazování pruhů s přesměrováním a AJAXovými požadavky v Tracy Baru.
Sekce
V čistém PHP je datové úložiště session realizováno jako pole dostupné přes globální proměnnou
$_SESSION
. Problém je v tom, že aplikace se běžně skládají z celé řady vzájemně nezávislých částí a
pokud všechny mají k dispozici jen jedno pole, dříve nebo později dojde ke kolizi názvů.
Nette Framework problém řeší tak, že celý prostor rozděluje na sekce (objekty Nette\Http\SessionSection). Každá jednotka pak používá svou sekci s unikátním názvem a k žádné kolizi již dojít nemůže.
Sekci získáme ze session:
$section = $session->getSection('unikatni nazev');
V presenteru stačí použít getSession()
s parametrem:
// $this je Presenter
$section = $this->getSession('unikatni nazev');
Ověřit existenci sekce lze metodou $session->hasSection('unikatni nazev')
.
Se samotnou sekcí se pak pracuje velmi snadno:
// zápis proměnné
$section['userName'] = 'franta'; // nebo $section->userName = 'franta';
// čtení proměnné
echo $section['userName']; // nebo echo $section->userName;
// zrušení proměnné
unset($section['userName']); // nebo unset($section->userName);
Preferovaný zápis je pomocí hranatých závorek, protože Nette pak lépe rozlišuje zápis od čtení a ví, kdy má automaticky nastartovat session.
Pro získání všech proměnných ze sekce je možné použít cyklus foreach
:
foreach ($section as $key => $val) {
echo "$key = $val";
}
Přístup k neexistující proměnné negeneruje žádnou chybu (vrácená hodnota je null). To však může být v určitých případech nežádoucí, proto existuje možnost, jak chování pro konkrétní sekci změnit:
$section->warnOnUndefined = true;
Nastavení expirace
Pro jednotlivé sekce nebo dokonce jednotlivé proměnné je možné nastavit expiraci. Můžeme tak nechat vypršet přihlášení uživatele za 20 minut, ale přitom si nadále pamatovat obsah košíku.
// sekce vyexpiruje po 20 minutách
$section->setExpiration('20 minutes');
// a proměnná $section['flash'] vyexpiruje už po 30 sekundách
$section->setExpiration('30 seconds', 'flash');
Kromě relativního času v sekundách lze použít UNIX timestamp nebo textový zápis. Zajímavostí je hodnota
0
, který nastaví expiraci na okamžik, kdy uživatel zavře prohlížeč. V prohlížeči Chrome je v nastavení
volba Po ukončení prohlížeče Google Chrome nechat aplikace na pozadí spuštěné, která je ve výchozím stavu
zapnutá. Způsobuje, že cookie, která má nastavenou expiraci do uzavření prohlížeče, se nesmaže.
// proměnná $section->password vyexpiruje při zavření prohlížeče
$section->setExpiration(0, 'password');
Nezapomeňte, že doba expirace celé session (viz konfigurace session) musí být stejná nebo vyšší než doba nastavená u jednotlivých sekcí či proměnných.
Zrušení dříve nastavené expirace docílíme metodou removeExpiration()
. Okamžité zrušení celé sekce
zajistí metoda remove()
.
Správa session
Přehled metod třídy Nette\Http\Session
pro správu session:
start(): void
Zahájí session.
isStarted(): bool
Je session zahájená?
close(): void
Ukončí session. Session se automaticky ukončí na konci běhu skriptu.
destroy(): void
Ukončí a smaže session.
exists(): bool
Obsahuje HTTP požadavek cookie se session ID?
regenerateId(): void
Vygeneruje nové náhodné session ID. Data zůstávají zachované.
getId(): string
Vrátí session ID.
Konfigurace
Session nastavujeme v konfiguraci. Pokud píšete aplikaci, která nepoužívá DI kontejner, slouží ke konfiguraci tyto metody. Musí být volány ještě před spuštěním session.
setName (string $name): static
Nastaví název cookie, ve které se přenáší session ID. Standardní název je PHPSESSID
. Hodí se
v případě, kdy v rámci jednoho webu provozujete několik odlišných aplikací.
getName(): string
Vrací název cookie, ve které se přenáší session ID.
setOptions (array $options): static
Konfiguruje session. Lze nastavovat všechny PHP session
direktivy (ve formátu camelCase, např. místo session.save_path
zapíšeme savePath
).
setExpiration (?string $time): static
Nastaví dobu neaktivity po které session vyexpiruje.
setCookieParameters (string $path, ?string $domain=null, ?bool $secure=null, ?string $samesite=null): static
Nastavení parametrů pro cookie. Výchozí hodnoty parametrů můžete změnit v konfiguraci.
setSavePath (string $path): static
Nastaví adresář, kam se ukládají soubory se session.
setHandler (\SessionHandlerInterface $handler): static
Nastavení vlastního handleru, viz dokumentace PHP.
Bezpečnost především
Server předpokládá, že komunikuje stále s tímtéž uživatelem, dokud požadavky doprovází stejné session ID. Úkolem bezpečnostních mechanismů je zajistit, aby tomu tak doopravdy bylo a nebylo možné identifikátor odcizit nebo podstrčit.
Nette Framework proto správně nakonfiguruje PHP direktivy, aby session ID přenášel pouze v cookie, znepřístupnil jej JavaScriptu a případné identifikátory v URL ignoroval. Navíc v kritických chvílích, jako je třeba přihlášení uživatele, vygeneruje session ID nové.
Pro konfiguraci PHP se používá funkce ini_set, kterou bohužel některé hostingy zakazují. Pokud je to případ i vašeho hostéra, pokuste se s ním domluvit, aby vám funkci povolil nebo alespoň server nakonfiguroval.