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

Дата обновления перевода 2023-07-20

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

Компонент 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');
// if the attribute may or may not exist, you can define a default value for it
$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>';
    }
}