Работа с сессиями в PHP

Сессия, механизм php, созданный для возможности передачи данных предназначенных конкретному пользователю при повторных запросах (веб-сервер не поддерживает постоянного соединения с клиентом, и каждый запрос обрабатывается, как новый, без какой-либо связи с предыдущими).

Принцип работы сессий: сервер выдает браузеру уникальный идентификатор, и просит передавать его с каждым запросом. Передача происходит стандартными способами, либо через куки, либо через переменные POST/GET. 

Идентификатор сессии — это обычная переменная, по умолчанию ее имя — PHPSESSID. Можно изменить директивой session.name в php.ini.

На сервере за передачу информации о сессиях отвечают две настройки в php.ini:

  • session.use_cookies — если равно 1, то PHP передает идентификатор в куках, если 0 — то нет.
  • session.use_trans_sid — если равно 1, то PHP передает его, добавляя к URL и формам, если 0 — то нет.

Соответственно, если включена только первая настройка и браузер отдает куки, то идентификатор передается через них, если не отдает, то сессия обнуляется при каждом запросе.

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

// в ссылках
<a href="/index.php?PHPSESSID=9ebca8bd62c830d3e79272b4f585ff8f">Index</a>

// в формах
<input type="hidden" name="PHPSESSID" value="00196c1c1a02e4c37ac04f921f4a5eec" />

Если включены обе, то браузеру выставляется кука, а ссылки и формы дополняются только если кука найдена не была.

Вся информация о сессии храниться в глобальном массиве $_SESSION.

Запись данных в сессию работает так:

// запускаем новую, либо возобновляем существующую сессию
session_start();

// передаем в массив сессий переменную с названием test и данными Hello world
$_SESSION['test']='Hello world!';

// если в качестве имени переменной хотим использовать значение переменной -
// пишем без кавычек или используем двойные 
$var = name;
$_SESSION["$var"]='Hello world!';

Используем например так:

// обычное условие проверки
if(!$_SESSION[$var]){
    echo "session variable is empty"
}

// можно получить id текущей сессии или ее имя
session_id(); 
session_name(); 

Удаление переменных из сессии:

unset($_SESSION[$var]);

// Если register_globals = on, надо добавить строку
session_unregister($var);

//Если надо сбросить все переменные сессии
session_unset();

Для закрытия сессии используется функция:

session_destroy()

Данные из глобального массива $_SESSION php хранит либо в файлах, путь к которым указывается в session.save_path в php.ini, либо в БД.

Для управления HTTP-заголовками отвечающими за кэш, используется функция session_cache_limiter(). Установка nocache, например, отменяет кэширование на стороне клиента.

Во время начала запроса режим кеширования сбрасывается до значения по умолчанию, хранящегося в session.cache_limiter. Таким образом, вам необходимо вызывать session_cache_limiter() для каждого запроса (перед тем, как вызвана функция session_start()).

Возможные значения:

Значение Посылаемый заголовок
public Expires: (когда-нибудь в будущем, в зависимости от session.cache_expire)
Cache-Control: public, max-age=(когда-нибудь в будущем, в зависимости от session.cache_expire)
Last-Modified: (временная метка последнего сохранения сессии)
private_no_expire Cache-Control: private, max-age=(session.cache_expire в будущем), pre-check=(session.cache_expire в будущем)
Last-Modified: (временная метка последнего сохранения сессии)
private Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: private, max-age=(session.cache_expire в будущем), pre-check=(session.cache_expire в будущем)
Last-Modified: (временная метка последнего сохранения сессии)
nocache Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Pragma: no-cache

Возможные проблемы

  • Вспомогательными вещами, вроде кодирования данных и удаления старых сессий, php занимается сам, и если возникает проблема с удалением информации о них, проверьте в php.ini строку session.gc_probability. Для того чтобы php мог самостоятельно удалять файлы сессий, должно быть установлено 1.
  • Warning: open(/tmp\sess_SID, O_RDWR) failed: No such file or directory (2) in full_script_path on line number
    или
    Warning: Failed to write session data (files). Please verify that the current setting of session.save_path is correct (/tmp))
    в этом случае надо в php.ini, в параметре session.save_path, указать правильный каталог, который существует и доступен для записи (не забудьте перезагрузить апач).
  • Warning: Cannot send session cookie — headers already sent.
    Warning: Cannot send session cache limiter — headers already sent.
    Warning: Cannot add header information — headers already sent.
    эти ошибки возникают в том случае, если браузер ранее уже получил заголовки для страницы. Функции header(), session_start(), setcookie() и вся логика, которая их вызывает, должны обрабатываться до любого вывода в браузер.
  • Если давать переменным скрипта имена, совпадающие с индексами массива $_SESSION, возможны проблемы. При register_globals=on значения будут перезаписывать друг друга. При register_globals=off, в случае, если в скрипте есть переменная сессии не имеющая значения, и глобальная переменная с тем же именем, появится ошибка «Your script possibly relies on a session side-effect which existed until PHP 4.2.3.». Для предотвращения этой ошибки, надо инициализировать переменные перед использованием или проверять на существование, и стараться не давать глобальным переменным имена, совпадающие с индексами массива $_SESSION.
  • Если вы используете перенаправление через header или навигацию с помощью JavaScript, PHP не пропишет необходимый идентификатор, т.к. он работает только со статичными ссылками. В этом случае надо проставлять идентификатор самостоятельно: 
    header("Location: /script.php?".session_name().'='.session_id());
  • Если один скрипт стартует сессию и долго выполняется, а другой пытается в это время стартовать её с тем же идентификатором, то он зависнет. Поэтому в долго выполняющихся скриптах следует стартовать сессию только тогда, когда она нужна, и тут же закрывать её, с помощью session_write_close().
tnx

Более детальный обзор можно найти на сайте phpfaq.ru