Сесії
HTTP — це протокол без стану, однак майже кожна програма потребує зберігати стан між запитами, наприклад, вміст кошика покупок. Саме для цього служать сесії або сеанси. Покажемо,
- як використовувати сесії
- як уникнути конфліктів імен
- як налаштувати термін дії
При використанні сесій кожен користувач отримує унікальний ідентифікатор, який називається ID сесії, що передається в cookie. Він служить ключем до даних сесії. На відміну від cookies, які зберігаються на стороні браузера, дані в сесії зберігаються на стороні сервера.
Сесію налаштовуємо в конфігурації, особливо важливим є вибір терміну дії.
Керування сесіями здійснює об'єкт Nette\Http\Session, до якого ви можете
отримати доступ, попросивши передати його за допомогою впровадження залежностей. У
презентерах достатньо лише викликати $session = $this->getSession()
.
Запуск сесії
Nette за замовчуванням автоматично розпочинає сесію в момент, коли ми
починаємо з неї читати або в неї записувати дані. Ручний запуск сесії
здійснюється за допомогою $session->start()
.
PHP надсилає при запуску сесії HTTP-заголовки, що впливають на кешування, див. session_cache_limiter, і, можливо, cookie з ID сесії. Тому необхідно завжди запускати сесію ще до надсилання будь-якого виводу в браузер, інакше буде викинуто виняток. Якщо ви знаєте, що під час відображення сторінки буде використовуватися сесія, запустіть її вручну заздалегідь, наприклад, у презентері.
У режимі розробки сесію запускає Tracy, оскільки вона використовує її для відображення смуг з перенаправленнями та AJAX-запитами в Tracy Bar.
Секції
У чистому 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', 'frank'); // Змінено на англійську для прикладу
// читання змінної, поверне 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-запит 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, яку, на жаль, деякі хостинги забороняють. Якщо це стосується і вашого хостера, спробуйте домовитися з ним, щоб він дозволив вам використовувати цю функцію або хоча б налаштував сервер.