Nette Http 3.1: mnohem chytřejší sessions

před 3 lety od David Grudl  

Nette vždy přistupovalo k session opatrným způsobem. Spouštělo je automaticky až když je uživatel potřeboval. Což konkrétně znamená:

  1. když do ní zapisoval
  2. když z ní četl a zároveň existovalo cookie se session ID

Protože pokud cookie neexistuje, nemusíme session spouštět, abychom věděli, že v ní žádná data nejsou. Tedy bez spuštění může Nette vrátit obsah nákupního košíku jako prázdný atd.

S automatickým spouštěním souvisí problém pozdního nastartování session, tedy v době, kdy už byl odeslán ze serveru výstup a již nelze posílat HTTP hlavičky. Při spuštění session se totiž odesílá cookie se session ID a také hlavičky Pragma a Expires. Aby se tento problém minimalizoval, Nette při nastavení autoStart: smart, což je výchozí, automaticky při startu spustilo session, pokud existovalo cookie se session ID.

Celé to má ovšem určitá úskalí, které verze 3.1.5 řeší.

Přístup k datům

K datům v session sekci se často přistupuje jako k proměnným:

$section = $session->getSection('unique name');
$section->userName = 'john';
echo $section->userName;

Kvůli specifickému technickému rysu PHP je však operace echo $section->userName detekována jako zápis. Tím pádem i výše zmíněné přečtení košíku $cart= $section->cart nastartuje session i když cookie neexistuje. Což kazí efektivitu autostartu.

Toto by bylo možné obejít použitím ArrayAccess syntaxe:

$section['userName'] = 'john';
echo $section['userName'];  // jde o operaci čtení

Nicméně syntaxe ArrayAccess může být uživatele příliš magická a někdy nefunguje tak, jak by se mohlo očekávat (např. $section['cart'][] = $item generuje notice Indirect modification of overloaded element has no effect). Proto v posledních verzích řady 3.1 i 3.0 najdete pro přístup k datům srozumitelnou sadu metod set(), get() a remove(). Pomocí set() lze nastavit i expiraci:

$section->set('flash', $message, '30 seconds');

Používání těchto metod se tak stává preferovaným přístupem k datům v sessions.

Nette upravuje chování v situaci, když se ukáže, že session ID je neplatné, tj. když neexistuje odpovídající soubor na disku apod. Nette takovou situaci odhalilo a vygenerovalo nové ID a čistý soubor, aby předešlo možnému podvržení ID ze strany útočníka. Nyní místo toho naopak cookie smaže a soubor vůbec nevytváří. Důvod je ten, aby se na disku nevytvářely prázdné soubory, pokud útočník záměrně session ID podvrhává.

Nová konfigurace autoStart

Volba autoStart může nově nabývat hodnot always a never. První z nich zapíná session vždy hned po startu a je totožná s variantou true. Novinkou je never, která zcela vypíná automatické startování a session se zapne jen tehdy, pokud zavoláte $session->start(). Výchozí variantou zůstává smart, která jak bylo uvedeno výše, už nezapíná session po startu, ale až v případě čtení nebo zápisu. Je vlastně tak totožná s false.

V řadě v3.0 se mění výchozí nastavení ze smart na false, což také vypíná autostart po spuštění.

Události $onStart, $onBeforeWrite

A nakonec objekt Nette\Http\Session má nově události $onStart a $onBeforeWrite, můžete tedy přidat callbacky, které se vyvolají po startu session nebo před jejím zápisem na disk a následným ukončením.

$session->onBeforeWrite[] = function () {
	// zapíšeme data do session
	$this->section->set('basket', $this->basket);
};

Při testování nového chování nezapomeňte, že 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.