Сессии и Cookies

Сессии и куки-файлы позволяют сохранять данные в нескольких пользовательских запросах. На простом PHP вы можете обращаться к ним через глобальные переменные $ _SESSION и $ _COOKIE, соответственно. Yii инкапсулирует сессии и куки-файлы как объекты и, следовательно, позволяет вам обращаться к ним объектно-ориентированным образом с помощью дополнительных полезных улучшений.

Сессии

Подобно запросам и ответам, вы можете получить доступ к сессиям через компонент приложения session, который по умолчанию является экземпляром yii\web\Session.

Открытие и закрытие сеансов

Чтобы открыть и закрыть сеанс, вы можете сделать следующее:

$session = Yii::$app->session;

// проверить, открыта ли сессия
if ($session->isActive) ...

// открыть сессию
$session->open();

// закрыть сессию
$session->close();

// уничтожает все данные, зарегистрированные на сессию.
$session->destroy();

Вы можете вызвать open() и close() несколько раз без ошибок; Внутренне методы сначала проверят, открыт ли сеанс.

Доступ к данным сессии

Чтобы получить доступ к данным, хранящимся в сессии, вы можете сделать следующее:

$session = Yii::$app->session;

// получить переменную сеанса. Следующие условия эквивалентны:
$language = $session->get('language');
$language = $session['language'];
$language = isset($_SESSION['language']) ? $_SESSION['language'] : null;

// установите переменную сеанса. Следующие условия эквивалентны:
$session->set('language', 'en-US');
$session['language'] = 'en-US';
$_SESSION['language'] = 'en-US';

// удалить переменную сеанса. Следующие условия эквивалентны:
$session->remove('language');
unset($session['language']);
unset($_SESSION['language']);

// проверьте, существует ли переменная сеанса. Следующие условия эквивалентны:
if ($session->has('language')) ...
if (isset($session['language'])) ...
if (isset($_SESSION['language'])) ...

// пересечь все переменные сеанса. Следующие условия эквивалентны:
foreach ($session as $name => $value) ...
foreach ($_SESSION as $name => $value) ...

При работе с данными сессии, которые являются массивами, компонент session имеет ограничение, которое запрещает вам непосредственно изменять элемент массива. Например:

$session = Yii::$app->session;

// следующий код НЕ будет работать
$session['captcha']['number'] = 5;
$session['captcha']['lifetime'] = 3600;

// следующий код работает:
$session['captcha'] = [
    'number' => 5,
    'lifetime' => 3600,
];

// следующий код также работает:
echo $session['captcha']['lifetime'];

Чтобы решить эту проблему, вы можете использовать один из следующих обходных путей:

$session = Yii::$app->session;

// напрямую используйте $_SESSION (убедитесь, что был вызван Yii::$app->session->open() )
$_SESSION['captcha']['number'] = 5;
$_SESSION['captcha']['lifetime'] = 3600;

// сначала получите весь массив, измените его и затем сохраните
$captcha = $session['captcha'];
$captcha['number'] = 5;
$captcha['lifetime'] = 3600;
$session['captcha'] = $captcha;

// используйте ArrayObject вместо массива
$session['captcha'] = new \ArrayObject;
...
$session['captcha']['number'] = 5;
$session['captcha']['lifetime'] = 3600;

// хранить данные массива ключами с общим префиксом
$session['captcha.number'] = 5;
$session['captcha.lifetime'] = 3600;

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

Хранилище пользовательских сессий

По умолчанию класс yii\web\Session сохраняет данные сесии в виде файлов на сервере. Yii также предоставляет следующие классы сессии, использующие другое хранилище сессий:

  • yii\web\DbSession: сохраняет данные сессии в таблице базы данных.
  • yii\web\CacheSession: сохраняет данные сессии в кеше с помощью настроенного компонента кэша.
  • yii\redis\Session: сохраняет данные сессии, используя redis в качестве носителя данных.
  • yii\mongodb\Session: сохраняет данные сессии в MongoDB.

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

Чтобы узнать, как настраивать и использовать эти классы компонентов, обратитесь к их документации по API. Ниже приведен пример, показывающий, как настроить yii\web\DbSession в конфигурации приложения для использования таблицы базы данных для хранения сессий:

return [
    'components' => [
        'session' => [
            'class' => 'yii\web\DbSession',
            // 'db' => 'mydb',  // идентификатор компонента приложения DB-соединения. По умолчанию используется 'db'.
            // 'sessionTable' => 'my_session', // имя таблицы сессии. По умолчанию используется 'session'.
        ],
    ],
];

Вам также необходимо создать следующую таблицу базы данных для хранения данных сессий:

CREATE TABLE session
(
    id CHAR(40) NOT NULL PRIMARY KEY,
    expire INTEGER,
    data BLOB
)

где «BLOB» относится к типу BLOB вашей предпочтительной СУБД. Ниже перечислены типы BLOB, которые можно использовать для некоторых популярных СУБД:

  • MySQL: LONGBLOB
  • PostgreSQL: BYTEA
  • MSSQL: BLOB

Данные Flash

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

Вы можете установить и получить доступ к флэш-данным через компонент приложения session. Например:

$session = Yii::$app->session;

// Request #1
// set a flash message named as "postDeleted"
$session->setFlash('postDeleted', 'You have successfully deleted your post.');

// Request #2
// display the flash message named "postDeleted"
echo $session->getFlash('postDeleted');

// Request #3
// $result will be false since the flash message was automatically deleted
$result = $session->hasFlash('postDeleted');

Как и обычные данные сессий, вы можете хранить произвольные данные как флэш-данные.

Когда вы вызываете yii\web\Session::setFlash(), он перезаписывает все существующие флэш-данные с тем же именем. Чтобы добавить новые флеш-данные к существующему сообщению с таким же именем, вы можете вместо этого вызвать yii\web\Session::addFlash(). Например:

$session = Yii::$app->session;

// Request #1
// add a few flash messages under the name of "alerts"
$session->addFlash('alerts', 'You have successfully deleted your post.');
$session->addFlash('alerts', 'You have successfully added a new friend.');
$session->addFlash('alerts', 'You are promoted.');

// Request #2
// $alerts is an array of the flash messages under the name of "alerts"
$alerts = $session->getFlash('alerts');

Для отображения Flash-сообщений вы можете использовать виджет загрузки Alert следующим образом:

echo Alert::widget([
   'options' => ['class' => 'alert-info'],
   'body' => Yii::$app->session->getFlash('postDeleted'),
]);

Cookies

Yii представляет каждый файл cookie как объект yii\web\Cookie. Оба yii\web\Request и yii\web\Response поддерживают коллекцию файлов cookie через свойство с именем cookie. Коллекция файлов cookie в первом представляет собой файлы cookie, представленные в запросе, в то время как коллекция файлов cookie в последнем представляет файлы cookie, которые должны быть отправлены пользователю.

Частью приложения, касающегося запроса и ответа, является контроллер. Поэтому куки должны быть прочитаны и отправлены в контроллер.

Чтение файлов cookie

Вы можете получить куки в текущем запросе, используя следующий код:

// get the cookie collection (yii\web\CookieCollection) from the "request" component
$cookies = Yii::$app->request->cookies;

// get the "language" cookie value. If the cookie does not exist, return "en" as the default value.
$language = $cookies->getValue('language', 'en');

// an alternative way of getting the "language" cookie value
if (($cookie = $cookies->get('language')) !== null) {
    $language = $cookie->value;
}

// you may also use $cookies like an array
if (isset($cookies['language'])) {
    $language = $cookies['language']->value;
}

// check if there is a "language" cookie
if ($cookies->has('language')) ...
if (isset($cookies['language'])) ...

Отправка файлов cookie

Вы можете отправить файлы cookie конечным пользователям, используя следующий код:

// get the cookie collection (yii\web\CookieCollection) from the "response" component
$cookies = Yii::$app->response->cookies;

// add a new cookie to the response to be sent
$cookies->add(new \yii\web\Cookie([
    'name' => 'language',
    'value' => 'zh-CN',
]));

// remove a cookie
$cookies->remove('language');
// equivalent to the following
unset($cookies['language']);

Помимо свойств name и value, показанных в приведенных выше примерах, класс yii\web\Cookie также определяет другие свойства, чтобы полностью отображать всю доступную информацию cookie, такую как domain, expire. Вы можете настроить эти свойства по мере необходимости, чтобы подготовить куки-файл, а затем добавить его в коллекцию cookie ответа.

Валидация cookie

Когда вы читаете и отправляете файлы cookie через компоненты request и response, вам нужна дополнительная безопасность проверки файлов cookie, которая защищает файлы cookie от изменения на стороне клиента. Это достигается подписью каждого файла cookie со строкой хеша, которая позволяет приложению определить, был ли изменен файл cookie на стороне клиента. Если это так, cookie НЕ будет доступен через коллекцию файлов cookie компонента request.

По умолчанию проверка валидации включена. Вы можете отключить его, установив свойство yii\web\Request::$enableCookieValidation как false, хотя мы настоятельно рекомендуем вам этого не делать.

При использовании валидации cookie вы должны указать yii\web\Request::$cookieValidationKey, который будет использоваться для генерации вышеупомянутых хеш-строк. Вы можете это сделать, настроив компонент request в конфигурации приложения:

return [
    'components' => [
        'request' => [
            'cookieValidationKey' => 'fill in a secret key here',
        ],
    ],
];