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