Работа с кукисами в Yii

11 августа 2011

Это адаптированный перевод wiki-страницы Cookie management in Yii.

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

Работа с кукисами

Чтение кукисов

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

$cookie = Yii::app()->request->cookies['cookie_name']->value;

Запись кукисов

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

Yii::app()->request->cookies['cookie_name'] = new CHttpCookie('cookie_name', $value);

Имейте ввиду, что название значения используется дважды. Один раз в конструкторе класса CHttpCookie и во второй раз в ассоциативном массиве cookies.

Удаление кукисов

Значение в кукисах можно удалить так:

unset(Yii::app()->request->cookies['cookie_name']);

Удалить вообще все значения в кукисах можно так:

Yii::app()->request->cookies->clear();

Имейте ввиду, что кукисы не будут удалены до следующей перезагрузки страницы вашего сайта.

Что нужно помнить?

Значения кукисов — это объекты

Значения кукисов в Yii хранятся в объектах класса CHttpCookie. Для того, чтобы получить реальное значение из кукисов нужно воспользоваться атрибутом value класса CHttpCookie. Если вы не сделаете этого, то получите ошибку или предупреждение о том, что объект класса CHttpCookie не может быть сконвертирован в строку.

Безопасное чтение

Так как уже было сказано о том, что кукисы в Yii — это объекты класса CHttpCookie, то в том случае, если нужного значения в кукисах не существует, то и соответствующий объект не будет создан. При попытке получить значение атрибута null-объекта произойдет ошибка Trying to get property of non-object. Чтобы обойти это всегда рекомендуется использовать тернарный оператор для чтения:

$cookie = (isset(Yii::app()->request->cookies['cookie_name']->value)) ?
    Yii::app()->request->cookies['cookie_name']->value : '';

Таким образом вы получите или нужное значение, или, в крайнем случае, просто пустую строку.

Код ниже показывает основной способ проверки того, существует ли определенное значение кукисов:

$is_cookie = !isset(Yii::app()->request->cookies['cookie_name']->value);

Перезагрузка страницы

Помните, что в действительности сервер ничего сам не делает с кукисами. Он только информирует браузер (в HTTP ответе) о том, что кукисы каким-то образом изменились. Браузер отвечает за большую часть работы с кукисами.

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

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

Проблема истечения кукисов

Если вы установите кукисы так, как показано в примерах выше, то они будут автоматически удалены как только вы закроете браузер (или не удалите значение кукисов вручную). Еще раз — это работа браузера, а не сервера. Вы должны закрыть все окна браузера, потому как только тогда все кукисы будут удалены.

Для того, чтобы предовратить удаление кукисов после закрытия окна браузера (примерно так, как это сделано в форме логина сайта Yii, после аутентификации в которой кукисы сохраняются на следующие 30 дней) вы должны установить их срок истечения. Пример:

$cookie = new CHttpCookie('cookie_name', $value);
$cookie->expire = time()+60*60*24*180; 
Yii::app()->request->cookies['cookie_name'] = $cookie;

Как видите мы устанавливаем дату истечения относительно текущего времени (первый подводный камень) и в виде UNIX timestamp, а не в виде форматированных даты и времени (второй подводный камень). Потому в примере выше мы используем PHP функцию time().

Мы считаем время истечения только в секундах (третий подводный камень), потому в примере выше дата будет равняться текущему времени плюс 180 дней (15 552 000 секунд). Для программиста, разумеется, понятнее будет если мы запишем эти полгода в виде математического выражения, а не просто в виде одного числа.

Количество секунд в разных, часто используемых, временных единицах:

  • час: 3 600 секунд;
  • день: 86 400 секунд;
  • неделя: 6 048 000 секунд;
  • месяц (30 дней): 2 592 000 секунд;
  • полгода (6 месяцев по 30 дней): 15 552 000 секунд;
  • год (12 месяцев по 30 дней): 31 104 000 секунд.

Кукисы в Javascript

Обычно, вы имеете доступ к вашим кукисам как из PHP кода, так и из Javascript кода. Так как Javascript выполняется в браузере, то и проблем с перезагрузкой страниц здесь нет.

httpOnly

Атрибут CHttpCookie.httpOnly определяет то, является ли значение кукисов доступным только в PHP коде. Значение по умолчанию «ложь» (false). При установке этого значения в «истину» (true) вы повышаете безопасность вашего приложения (это предовращает возможность получения значения кукисов посредством XSS), но ваши кукисы не будут доступны из Javascript.

jQuery

Если вы используете jQuery в своих проектах, то существует специальный плагин для работы с кукисами. Плагин jquery.cookie.js поставляется вместе с Yii и очень сильно упрощает работу с кукисами из Javascript. Пример установки значения кукисов:

$.cookie('the_cookie', 'the_value');

Для того, чтобы получить значение из кукисов:

var cookie = $.cookie('the_cookie');

Удаление кукисов:

$.cookie('the_cookie', null);

В файле jquery.cookie.js содержится большее количество примеров и более подробная документация по его использованию.

Способ без использования jQuery

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

function readCookie(cookieName)
{
    var theCookie = '' + document.cookie;
    var ind = theCookie.indexOf(cookieName);
    var ind1 = theCookie.indexOf(';', ind);
    if(ind1 == -1) ind1 = theCookie.length;
 
    if(ind == -1 || cookieName == '') return '';
 
    return unescape(theCookie.substring(ind + cookieName.length + 1, ind1));
}
 
function setCookie(cookieName, cookieValue, nDays)
{
    var today = new Date();
    var expire = new Date();
 
    if(nDays == null || nDays == 0) nDays = 1;
 
    expire.setTime(today.getTime() + 3600000 * 24 * nDays);
 
    document.cookie = cookieName + '=' + escape(cookieValue) + ';expires=' + expire.toGMTString();
}

Время в кукисах в Javascript задается в миллисекундах, потому не забываем домножать секунда на 1 000.

Дополнительная информация

Дополнения, найденные неточности и ошибки в тексте приветствуются.