Сесии

HTTP е протокол без състояние, но почти всяко приложение трябва да съхранява състояние между заявките, например съдържанието на количката за пазаруване. Именно за това служат сесиите. Ще покажем,

  • как да използваме сесии
  • как да предотвратим конфликти на имена
  • как да настроим изтичане

При използване на сесии всеки потребител получава уникален идентификатор, наречен session ID, който се предава в бисквитка. Той служи като ключ към данните на сесията. За разлика от бисквитките, които се съхраняват от страна на браузъра, данните в сесията се съхраняват от страна на сървъра.

Сесията се настройва в конфигурацията, важен е особено изборът на времето за изтичане.

Управлението на сесията се осъществява от обекта Nette\Http\Session, до който можете да стигнете, като го получите чрез dependency injection. В презентерите е достатъчно само да извикате $session = $this->getSession().

Инсталация и изисквания

Стартиране на сесия

Nette по подразбиране автоматично стартира сесията в момента, когато започнем да четем от нея или да записваме данни в нея. Ръчно сесията се стартира с $session->start().

PHP изпраща при стартиране на сесията HTTP хедъри, влияещи на кеширането, виж session_cache_limiter, и евентуално и бисквитка със session ID. Затова е необходимо винаги да стартирате сесията преди изпращането на какъвто и да е изход към браузъра, иначе ще бъде хвърлено изключение. Ако знаете, че по време на рендирането на страницата ще се използва сесия, стартирайте я ръчно преди това, например в презентера.

В режим на разработка сесията се стартира от Tracy, тъй като я използва за показване на ленти с пренасочвания и AJAX заявки в Tracy Bar.

Секции

В чист PHP хранилището на данни на сесията се реализира като масив, достъпен чрез глобалната променлива $_SESSION. Проблемът е, че приложенията обикновено се състоят от цяла редица взаимно независими части и ако всички имат на разположение само един масив, рано или късно ще възникне конфликт на имена.

Nette Framework решава проблема, като разделя цялото пространство на секции (обекти Nette\Http\SessionSection). Всяка единица след това използва своя собствена секция с уникално име и вече не може да възникне никакъв конфликт.

Секцията получаваме от сесията:

$section = $session->getSection('уникално_име');

В презентера е достатъчно да използвате getSession() с параметър:

// $this е Presenter
$section = $this->getSession('уникално_име');

Проверката за съществуване на секция може да се направи с метода $session->hasSection('уникално_име').

Със самата секция след това се работи много лесно с помощта на методите set(), get() и remove():

// запис на променлива
$section->set('userName', 'franta');

// четене на променлива, връща null, ако не съществува
echo $section->get('userName');

// изтриване на променлива
$section->remove('userName');

За получаване на всички променливи от секцията може да се използва цикъл foreach:

foreach ($section as $key => $val) {
	echo "$key = $val";
}

Настройка на изтичане

За отделни секции или дори отделни променливи е възможно да се настрои изтичане. Можем така да оставим изтичането на влизането на потребителя след 20 минути, но същевременно да продължим да помним съдържанието на количката.

// секцията изтича след 20 минути
$section->setExpiration('20 minutes');

За настройка на изтичането на отделни променливи служи третият параметър на метода set():

// променливата 'flash' изтича след 30 секунди
$section->set('flash', $message, '30 seconds');

Не забравяйте, че времето за изтичане на цялата сесия (виж конфигурация на сесията) трябва да бъде същото или по-голямо от времето, зададено за отделните секции или променливи.

Отмяната на предишно зададено изтичане се постига с метода removeExpiration(). Незабавното отменяне на цялата секция осигурява методът remove().

Събития $onStart, $onBeforeWrite

Обектът Nette\Http\Session има събития $onStart и $onBeforeWrite, така че можете да добавите callback-ове, които се извикват след стартиране на сесията или преди нейното записване на диска и последващо прекратяване.

$session->onBeforeWrite[] = function () {
	// записваме данни в сесията
	$this->section->set('basket', $this->basket);
};

Управление на сесии

Преглед на методите на класа Nette\Http\Session за управление на сесии:

start(): void

Стартира сесията.

isStarted(): bool

Сесията стартирана ли е?

close(): void

Прекратява сесията. Сесията автоматично се прекратява в края на изпълнението на скрипта.

destroy(): void

Прекратява и изтрива сесията.

exists(): bool

HTTP заявката съдържа ли бисквитка със session ID?

regenerateId(): void

Генерира нов случаен session ID. Данните остават запазени.

getId(): string

Връща session ID.

Конфигурация

Сесията се настройва в конфигурацията. Ако пишете приложение, което не използва DI контейнер, за конфигурация служат тези методи. Трябва да бъдат извикани преди стартирането на сесията.

setName (string $name): static

Задава името на бисквитката, в която се пренася session ID. Стандартното име е PHPSESSID. Полезно е в случай, че в рамките на един сайт поддържате няколко различни приложения.

getName(): string

Връща името на бисквитката, в която се пренася session ID.

setOptions (array $options)static

Конфигурира сесията. Могат да се настройват всички PHP session директиви (във формат camelCase, напр. вместо session.save_path записваме savePath) и също readAndClose.

setExpiration (?string $time)static

Задава времето на неактивност, след което сесията изтича.

setCookieParameters (string $path, ?string $domain=null, ?bool $secure=null, ?string $samesite=null)static

Настройка на параметрите за бисквитката. Стойностите по подразбиране на параметрите можете да промените в конфигурацията.

setSavePath (string $path)static

Задава директорията, където се съхраняват файловете със сесиите.

setHandler (\SessionHandlerInterface $handler)static

Настройка на собствен handler, виж документацията на PHP.

Сигурността преди всичко

Сървърът предполага, че комуникира постоянно със същия потребител, докато заявките са придружени от същия session ID. Задачата на механизмите за сигурност е да гарантират, че това наистина е така и не е възможно идентификаторът да бъде откраднат или подменен.

Nette Framework затова правилно конфигурира PHP директивите, така че session ID да се пренася само в бисквитка, да го направи недостъпен за JavaScript и да игнорира евентуални идентификатори в URL. Освен това в критични моменти, като например влизане на потребителя, генерира нов session ID.

За конфигурация на PHP се използва функцията ini_set, която за съжаление някои хостинги забраняват. Ако това е случаят и с вашия хостинг, опитайте да се договорите с него да ви разреши функцията или поне да конфигурира сървъра.

версия: 4.0