Сессии
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, которую, к сожалению, некоторые хостинги запрещают. Если это ваш случай, попробуйте договориться с хостером, чтобы он разрешил вам эту функцию или хотя бы настроил сервер.