Сеансы

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

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

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

Мы настраиваем сессию в конфигурации, при этом важен выбор времени истечения срока действия.

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

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

Запуск сеанса

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

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

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

Раздел

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

Nette Framework решает эту проблему путем разделения всего пространства на секции (объекты Nette\Http\SessionSection). При этом каждая часть использует свой собственный раздел с уникальным именем, и коллизии не возникают.

Мы получаем раздел от менеджера сессий:

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

В презентере достаточно вызвать getSession() с параметром:

// $this - Presenter
$section = $this->getSession('unique name');

Существование раздела можно проверить методом $session->hasSection('unique name').

С самим разделом очень легко работать, используя методы 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 с идентификатором сессии?

regenerateId(): void

Генерирует новый случайный идентификатор сессии. Данные остаются неизменными.

getId(): string

Возвращает идентификатор сессии.

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

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

setName(string $name): static

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

getName(): string

Возвращает имя сеансового файла cookie.

setOptions(array $options)static

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

setExpiration(?string $time)static

Устанавливает время бездействия, после которого сессия завершается.

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

Устанавливает параметры для куки. Значения параметров по умолчанию можно изменить в разделе configuration.

setSavePath(string $path)static

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

setHandler(\SessionHandlerInterface $handler)static

Устанавливает пользовательский обработчик, см. документацию PHP.

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

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

Именно поэтому Nette Framework правильно настраивает директивы PHP для передачи идентификатора сессии только в cookies, для предотвращения доступа из JavaScript и для игнорирования идентификаторов в URL. Более того, в критические моменты, такие как вход пользователя в систему, он генерирует новый идентификатор сессии.

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