Сессии

HTTP — это протокол без состояния, однако почти каждое приложение нуждается в сохранении состояния между запросами, например, содержимого корзины покупок. Именно для этого служат сессии. Мы покажем,

  • как использовать сессии
  • как избежать конфликтов имен
  • как установить срок действия

При использовании сессий каждый пользователь получает уникальный идентификатор, называемый ID сессии, который передается в cookie. Он служит ключом к данным сессии. В отличие от cookie, которые хранятся на стороне браузера, данные в сессии хранятся на стороне сервера.

Сессию мы настраиваем в конфигурации, особенно важен выбор срока действия (expiration).

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

Установка и требования

Запуск сессии

Nette по умолчанию автоматически запускает сессию в тот момент, когда мы начинаем читать из нее или записывать в нее данные. Вручную сессия запускается с помощью $session->start().

PHP при запуске сессии отправляет HTTP-заголовки, влияющие на кеширование, см. session_cache_limiter, и, возможно, cookie с ID сессии. Поэтому необходимо всегда запускать сессию еще до отправки любого вывода в браузер, иначе будет выброшено исключение. Если вы знаете, что в процессе рендеринга страницы будет использоваться сессия, запустите ее вручную заранее, например, в презентере.

В режиме разработки сессию запускает Tracy, так как он использует ее для отображения полос с перенаправлениями и AJAX-запросами в Tracy Bar.

Секции

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

Nette Framework решает эту проблему, разделяя все пространство на секции (объекты api: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, поэтому вы можете добавить колбэки, которые будут вызваны после запуска сессии или перед ее записью на диск и последующим завершением.

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

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

Обзор методов класса Nette\Http\Session для управления сессией:

start(): void

Запускает сессию.

isStarted(): bool

Запущена ли сессия?

close(): void

Завершает сессию. Сессия автоматически завершается в конце выполнения скрипта.

destroy(): void

Завершает и удаляет сессию.

exists(): bool

Содержит ли HTTP-запрос cookie с ID сессии?

regenerateId(): void

Генерирует новый случайный ID сессии. Данные остаются сохраненными.

getId(): string

Возвращает ID сессии.

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

Сессию настраиваем в конфигурации. Если вы пишете приложение, которое не использует DI-контейнер, для конфигурации служат следующие методы. Они должны быть вызваны еще до запуска сессии.

setName (string $name): static

Устанавливает имя cookie, в котором передается ID сессии. Стандартное имя — PHPSESSID. Пригодится в случае, когда в рамках одного веб-сайта вы запускаете несколько различных приложений.

getName(): string

Возвращает имя cookie, в котором передается ID сессии.

setOptions (array $options)static

Конфигурирует сессию. Можно устанавливать все PHP директивы сессии (в формате camelCase, например, вместо session.save_path запишем savePath) и также readAndClose.

setExpiration (?string $time)static

Устанавливает время неактивности, после которого сессия истечет.

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

Настройка параметров для cookie. Значения по умолчанию параметров вы можете изменить в конфигурации.

setSavePath (string $path)static

Устанавливает каталог, куда сохраняются файлы сессий.

setHandler (\SessionHandlerInterface $handler)static

Установка собственного обработчика, см. документацию PHP.

Безопасность прежде всего

Сервер предполагает, что он постоянно взаимодействует с одним и тем же пользователем, пока запросы сопровождаются одним и тем же ID сессии. Задачей механизмов безопасности является обеспечение того, чтобы это действительно было так, и чтобы идентификатор нельзя было украсть или подменить.

Поэтому Nette Framework правильно конфигурирует PHP-директивы, чтобы ID сессии передавался только в cookie, делал его недоступным для JavaScript и игнорировал возможные идентификаторы в URL. Кроме того, в критические моменты, такие как вход пользователя, он генерирует новый ID сессии.

Для конфигурации PHP используется функция ini_set, которую, к сожалению, некоторые хостинги запрещают. Если это ваш случай, попробуйте договориться с хостером, чтобы он разрешил вам эту функцию или хотя бы настроил сервер.

версия: 4.0