Управління сесіями

Дата оновлення перекладу 2022-04-30

Управління сесіями

Компонент Symfony HttpFoundation має дуже потужну та гнучку підсистему сесій, яка спроектована для надання можливості управління сесіями через простий об'єктно-орієнтований інтерфейс, з використанням різноманітних драйверів зберігання сесій.

Сесії використовуються через просту реалізацію Session інтерфейсу SessionInterface.

Caution

Переконайтеся в тому, що ваша PHP-сесія ще не запущена перед тим, як використовувати клас Session. Якщо у вас є успадкована система сесій, яка запускає вашу сесію, див. Успадковані сесії.

Швидкий приклад:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
use Symfony\Component\HttpFoundation\Session\Session;

$session = new Session();
$session->start();

// встановлювати та отримувати атрибути сесії
$session->set('name', 'Drak');
$session->get('name');

// встановклювати флеш-повідомлення
$session->getFlashBag()->add('notice', 'Profile updated');

// добувати повідомлення
foreach ($session->getFlashBag()->get('notice', []) as $message) {
    echo '<div class="flash-notice">'.$message.'</div>';
}

Note

Сесії Symfony створені для заміни декількох оригінальних PHP-функцій. Додаткам варто уникати використання session_start(), session_regenerate_id(), session_id(), session_name(), та session_destroy(), а замість цього використовувати API в наступному розділі.

Note

Незважаючи на те, що рекомендується запускати сесію чітко, насправді вона почнеться за вимогою, тобто, якщо буде створено запит до сесії прочитати або записати дані сесії.

Caution

Сесії Symfony несумісні з директивою php.ini session.auto_start = 1. Цю директиву необхідно вимкнути в php.ini, в директивах веб-серверу або в .htaccess.

API Сесії

Клас Session реалізує SessionInterface.

Session має простий API, який розділяється на декілька груп.

Робочий процесс сесії

start()
Запускає сесію - не використовуйте session_start().
migrate()
Повторно генерує ID сесії - не використовуйте session_regenerate_id(). Цей метод може опціональни змінювати час життя нового куки, який буде вилучено шляхом виклику цього методу.
invalidate()
Очищує всі дані сесії та повторно генерує ID сесії. Не використовуйте session_destroy().
getId()
Отримує ID сесії. Не використовуйте session_id().
setId()
Встановлює ID сесії. Не використовуйте session_id().
getName()
Отримує ім'я сесії. Не використовуйте session_name().
setName()
Встановлює ім'я сесії. Не використовуйте session_name().

Атрибути сесії

set()
Встановлює атрибут за ключем.
get()
Отримує атрибут за ключем.
all()
Отримує усі атрибути у вигляді масиву ключ => значення.
has()
Повертає "true", якщо атрибут існує.
replace()
Встановлює декілька атрибутів одночасно: бере масив ключів та встановлює в кожному пару ключ => значення.
remove()
Видаляє атрибут за ключем.
clear()
Очистити всі атрибути.

Атрибути зберігаються внутрішньо в "Сумці", PHP-об'єкті, який діє як масив. Для управління "Сумкою" існує декілька методів:

registerBag()
Реєструє SessionBagInterface.
getBag()
Отримує SessionBagInterface за іменем сумки.
getFlashBag()
Отримує FlashBagInterface. Це просто скорочення для зручності.

Метадані сесії

getMetadataBag()
Отримує MetadataBag, який містить інформацію про сесію.

Управління даними сесії

Управління PHP-сесією потребуе використання суперглобального $_SESSION, однак, це дещо протиречить можливості тестування коду та іканспуляції в парадигмі OOP. Щоб подолат це, Symfony використовує сумки сесії, пов'язані з сесією, щоб інкапсулювати конкретний набір даних атрибутів або флеш-повідомлень.

Такий підхід також зменшує засмічення простору імен в рамках суперглобального $_SESSION, так як кожна сумка зберігає всі свої дані під унікальним простором імен. Це дозволяє Symfony мирно співіснувати з іншими додатками та бібліотеками, які можуть використовувати суперглобальний $_SESSION, і всі дані залишаються повністю сумісними з управлінням сесій Symfony.

Symfony надає два види сумок зберігання, з двома різними реалізаціями. Все написано з урахуванням інтерфейсів, тому ви можете розширити або створити ваші власні види сумок, за необхідності.

SessionBagInterface має наступний API, який в основному призначено для внутрішніх цілей:

getStorageKey()
Повертає ключ, під яким сумка зберігатиме свій масив в $_SESSION. Зазвичай це значення може залишитися в стані за замовчуванням, та використовується лише внутрішньо.
initialize()
Викликається внутрішньо класами зберігання сесій Symfony, щоб пов'язати дані сумки з сесією.
getName()
Повертає ім'я сумки сесії.
clear()
Очищує дані з сумки.

Атрибути

Ціллю сумок, що реалізують AttributeBagInterface, є обробка сховища атрибутів сесії. Це може включати в себе такі речі, як ID користувача, налаштування входу "запам'ятати мене" або іншу інформацію, засновану на стані користувача.

AttributeBag
Це стандартна реалізація за замовчуванням.
NamespacedAttributeBag

Ця реалізація дозволяє зберігання атрибутів в структурованому просторі імен.

5.3

Клас NamespacedAttributeBag застарів у Symfony 5.3. Якщо вам потрібна ця функція, вам необхідно буде реалізувати клас самостійно.

AttributeBagInterface
має API
set()
Встановлює атрибут за іменем (set('name', 'value')).
get()
Отримує атрибут за іменем (get('name')) та може визначити значення за замовчуванням, якщо атрибуту не існує (get('name', 'default_value')).
all()
Отримує всі атрибути у вигляді асоціативного масиву name => value.
has()
Повертає true, якщо атрибут існує.
replace()
Встановлює декілька атрибутів одночасно, використовуючи асоціативний масив (name => value). Якщо атрибути вже існували, вони замінюються; якщо ні - створюються.
remove()
Видаляє атрибут за іменем та повертає його значення.
clear()
Видаляє всі атрибути.

Приклад:

1
2
3
4
5
6
7
8
9
10
11
12
use Symfony\Component\HttpFoundation\Session\Attribute\AttributeBag;
use Symfony\Component\HttpFoundation\Session\Session;
use Symfony\Component\HttpFoundation\Session\Storage\NativeSessionStorage;

$session = new Session(new NativeSessionStorage(), new AttributeBag());
$session->set('token', 'a6c1e0b6');
// ...
$token = $session->get('token');
// якщо атрибут може як існувати, так і ні, ви можете визначити для нього значення за замовчуванням
$token = $session->get('attribute-name', 'default-attribute-value');
// ...
$session->clear();

Атрибути з простором імен

Будь-яка система зберігання значень ключів обмежена в тому, наскільки складні дані можуть бути збережені, так як кожний ключ має бути унікальним. Ви можете досягти простору імен, надавши ключам угоду про іменування, щоб різні частини вашого додатку могли працювати, не стикаючись одне з одним. Наприклад, module1.foo та module2.foo. Однак, інколи це не дуже практично, коли дані атрибутів являють собою масив, наприклад, набір токенів. В такому випадку, управління масивом стає обтяжливим, так як вам необхідно добувати масив, обробляти його, а потім знову зберігати:

1
2
3
4
5
6
$tokens = [
    'tokens' => [
        'a' => 'a6c1e0b6',
        'b' => 'f4a7b1f3',
    ],
];

Тому будь-яка обробка цього може стати огидною, навіть за умови додавання токена до масику:

1
2
3
$tokens = $session->get('tokens');
$tokens['c'] = $value;
$session->set('tokens', $tokens);

5.3

Клас NamespacedAttributeBag застарів у Symfony 5.3. Якщо вам потрібна ця функція, вам необхідно буде реалізувати клас самостійно.

Зі структурованим простором імен, ключ може бути переведений в структуру масиву наступним чином, використовуючи символ простору імен (за замовчуванням /):

1
2
3
4
5
// ...
use Symfony\Component\HttpFoundation\Session\Attribute\NamespacedAttributeBag;

$session = new Session(new NativeSessionStorage(), new NamespacedAttributeBag());
$session->set('tokens/c', $value);

Флеш-повідомлення

Ціль FlashBagInterface - надати спосіб установки та добування повідомлень на основі кожної сесії. Звичайний робочий процес виглядав би як установка флеш-повідомлень в запиті, та його відображення після перенаправлення сторінки. Наприклад, користувач відправляє форму, яка задіює контролер оновлень, а після обробки контролер пенеправляє сторінку або на оновлену сторінку, або на сторінку помилки. Флеш-повідомлення, встановлені в запиті попередньої сторінки, будуть відображені одразу ж після того, як буде завантажена наступна сторінка для цієї сесії. Однак, це лише одне з застосувань флеш-повідомлень.

AutoExpireFlashBag
В цій реалізації повідомелння, встановлені в одному завантаженні сторінки, будуть доступні для відображення лише при наступному завантаженні сторінки. Строк дії таких повідомлень автоматично закінчиться, незалежно від того, чи будуть вони добуті, чи ні.
FlashBag
В цій реалізації, повідомлення будуть залишатися в сесії до тих пір, поки їх чітко не добудуть або не очистять. Це дозволяє використовувати ESI-кешування.
FlashBagInterface
має простий API.
add()
Додає флеш-повідомлення в стек вказаного типу.
set()
Встановлюе флеші по типу; Цей метод зручним чином бере як поодинокі повідомелння в якості string, так і декілька повідомлень в якості array.
get()
Отримує флеші по типу та очущеє ті флеші в сумці.
setAll()
Встановлює всі флеші, приймає масив масивів з ключами type => array(messages).
all()
Отримує всі флеші (як масив масивів з ключами) та очищує флеші з сумки.
peek()
Отримує флеші по типу (тільки для читання).
peekAll()
Отримує всі флеші (тільки для читання) в якості масиву масивів з ключами.
has()
Повертає "true", якщо тип існує, і "false" - якщо ні.
keys()
Повертає масив збережених типів флешів.
clear()
Очищує сумку.

Для звичайних додатків зазвичай достатньо мати одне флеш-повідомлення на тип, наприклад, повідомлення про підтвердження після відправки форми. Однак, флеш-повідомлення зберігаються в масиві з ключами по $type флешу, що означає, що ваш додаток може викликати декілька повідомлень на заданий тип. Це дозволяє використання API для більш складних повідомлень в вашому додатку.

Приклади установки декілької флешів:

1
2
3
4
5
6
7
8
9
10
11
12
use Symfony\Component\HttpFoundation\Session\Session;

$session = new Session();
$session->start();

// додати флеш-повідомлення
$session->getFlashBag()->add(
    'warning',
    'Your config file is writable, it should be set read-only'
);
$session->getFlashBag()->add('error', 'Failed to update name');
$session->getFlashBag()->add('error', 'Another error');

Відображення флеш-повідомлень може виглядати наступним чином.

Просто відобразити один тип повідомлень:

1
2
3
4
5
6
7
8
9
// відобразити попередження
foreach ($session->getFlashBag()->get('warning', array()) as $message) {
    echo '<div class="flash-warning">'.$message.'</div>';
}

// відобразити помилки
foreach ($session->getFlashBag()->get('error', array()) as $message) {
    echo '<div class="flash-error">'.$message.'</div>';
}

Компактний метод для обробки відображення всіх флешів одночасно:

1
2
3
4
5
foreach ($session->getFlashBag()->all() as $type => $messages) {
    foreach ($messages as $message) {
        echo '<div class="flash-'.$type.'">'.$message.'</div>';
    }
}