Как устроены, и как работают сессии php?

Для начала надо как-то идентифицировать браузер. Для этого надо выдать ему уникальный идентификатор и попросить передавать его с каждым запросом. Стыдно признаться, но когда я впервые узнал о сессиях, я думал, что это какой-то особый механизм, некий новый способ общения браузера с сервером — «сессии». Что идентификатор сессии передается каким-то особым образом. Но, разочарование было жестоким…

Сессии используют стандартные, хорошо известные способы передачи данных. Собственно, других-то просто и нет.
Идентификатор — это обычная переменная. По умолчанию ее имя — PHPSESSID.
Задача PHP отправить ее браузеру, чтобы тот вернул ее со следующим запросом. Из уже упоминавшегося раздела FAQ ясно, что переменную можно передать только двумя способами: в cookies или POST/GET запросом.
PHP использует оба варианта.

За это отвечают две настройки в php.ini:

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

Менять эти и другие параметры сессий можно так же, как и другие настройки PHP — в файле php.ini, а так же с помощью команды ini_set() или в файлах настройки веб-сервера

Если включена только первая, то при старте сессии (при каждом вызове session_start()) клиенту устанавливается cookies. Браузер исправно при каждом следующем запросе эту cookies возвращает и PHP имеет идентификатор сессии. Проблемы начинаются, если браузер cookies не возвращает. В этом случае, не получая cookies с идентификатором, PHP будет все время стартовать новую сессию, и механизм работать не будет.

Если включена только вторая, то cookies не выставляется. А происходит то, ради чего, в основном, собственно, и стоит использовать встроенный механизм сессий. После того, как скрипт выполняет свою работу, и страница полностью сформирована, PHP просматривает ее всю и дописывает к каждой ссылке и к каждой форме передачу идентификатора сессии. Это выглядит примерно так:

Index

превращается в

Index

а к формам добавляется скрытое поле

И браузер при клике на любую ссылку, или при нажатии на кнопку в форме, пошлет в запросе нужную нам переменную — идентификатор сессии!

Теоретически, в наших с вами самодельных сессиях на cookies и базе, можно самому, руками приписать ко всем ссылками передачу ид — и тогда наши собственные сессии будут работать независимо от cookies. Но, согласитесь — приятнее, когда эту работу делает кто-то другой? 😉

По умолчанию в последних версиях PHP включены обе опции. Как PHP поступает в этом случае? Кука выставляется всегда. А ссылки автодополняются только если РНР не обнаружил cookies с идентификатором сессии. Когда пользователь в првый раз за этот сеанс заходит на сайт, ему ставится cookies, и дополняются ссылки. При следующем запросе, если cookies поддерживаются, PHP видит cookies и перестает дополнять ссылки. Если cookies не работают, то PHP продолжает исправно добавлять ид к ссылкам, и сессия не теряется.
Пользователи, у которых работают cookies, увидят длинную ссылку с ID только один раз.

С передачей идентификатора закончили. Теперь осталось привязать к нему файл с данными на стороне сервера. PHP это сделает за нас. Достаточно просто написать:

session_start();
$_SESSION[‘test’]=’Hello world!';

И PHP запишет в файл, связанный с этой сессией, переменную test.

Здесь очень важное замечание.

Массив $_SESSION — особенный.
В нем, собственно, и находятся переменные, которые мы ходим сделать доступными в различных скриптах.
Чтобы поместить переменную в сессию, достаточно присвоить ее элементу массива $_SESSION.
Чтобы получить ее значение — достаточно обратиться к тому же элементу. Пример будет чуть ниже.

Cборкой мусора — удалением устаревших файлов PHP тоже занимается сам. Как и кодированием данных и кучей всяких других нужных вещей. В результате этой заботы работа с сессиями оказывается очень простой.
Вот мы, собственно, и подошли к примеру работы сессий.
Пример очень маленький:

session_start();
if (!isset($_SESSION[‘counter’])) $_SESSION[‘counter’]=0;
echo «Вы обновили эту страницу «.$_SESSION[‘counter’]++.» раз. «;
echo »
обновить»;
?>

Мы проверяем, есть ли у нас в сессии переменная counter, если нет, то создаем ее со значением 0, а дальше выводим ее значение и увеличиваем на единицу. Увеличенное значение запишется в сессию, и при следующем вызове скрипта переменная будет иметь значение 1, и так далее. Все очень просто.

Для того, чтобы иметь доступ к переменным сессии на любых страницах сайта, надо написать ТОЛЬКО ОДНУ(!) строчку в самом начале КАЖДОГО файла, в котором нам нужны сессии:

session_start();

И далее обращаться к элементам массива $_SESSION. Например, проверка авторизации будет выглядеть примерно так:

session_start();
if ($_SESSION[‘authorized’]1) {
header(«Location: /auth.php»);
exit;
}

Удаление переменных из сессии. Если у вас register_globals=off, то достаточно написать

unset($_SESSION[‘var’]);

Если же нет, то тогда рядом с ней надо написать:

session_unregister(‘var’);

Область применения

Очень важно понимать, для чего сессии стоит использовать, а для чего — нет.

Во-первых, помните, что сессии можно применять только тогда, когда они нужны самому пользователю, а не для того, чтобы чинить ему препятствия. Ведь он в любой момент может избавиться от идентификатора!
Скажем, при проверке на то, что заполняет форму человек, а не скрипт, пользователь сам заинтересован в том, чтобы сессия работала — иначе он не сможет отправить форму! А вот для ограничения количества запросов к скрипту сессия уже не годится — злонамеренный скрипт просто не будет возвращать идентификатор.

Во-вторых. Важно четко себе представлять тот факт, что сессия — это сеанс работы с сайтом, так как его понимает человек. Пришел, поработал, закрыл браузер — сессия завершилась. Как сеанс в кино. Хочешь посмотреть еще один – покупай новый билет. Стартуй новый сеанс. Этому есть и техническое объяснение. Гарантированно механизм сессий работает только именно до закрытия браузера. Ведь у клиента могут не работать cookies, а в этом случае, естественно, все дополненные идентификатором ссылки пропадут с его закрытием.

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

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

— по факту идентификации пользователя стартует сессия и признак авторизованности передается в ней.
— Если надо «запомнить» пользователя, то ему ставится cookies, его идентифицирующая.
— При следующем заходе пользователя на сайт, для того, чтобы авторизоваться, он должен либо ввести пароль, либо система сама его опознает по поставленной ранее cookies, и стартует сессию. Новую сессию, а не продолжая старую.

В-третьих, не стоит стартовать сессии без разбору, каждому входящему на сайт. Это создаст совершенно лишнюю нагрузку. Не используйте сессии по пустякам – к примеру, в счетчиках. То, что спайлог называет сессиями, считается, конечно же, на основе статистики заходнов, а не с помощью механизма сессий, аналогичного PHP.

К тому же, возьмем поисковик, который индексирует ваш сайт. Если поисковый робот не поддерживает cookies, то PHP по умолчанию будет поставлять к ссылкам PHPSESSID, что может не сильно понравится поисковику, который, по слухам, и так-то динамические ссылки не жалует, а тут вообще при каждом заходе — новый адрес!

Если сессии используются для ограничения доступа к закрытому разделу сайта, то все просто поисковик и не должен его индексировать. Если же приходится показывать одну и ту же страницу как авторизованным, так и не авторизованным пользователям, то тут поможет такой трюк – стартовать сессию только тем, кто ввел пароль, или тем, у кого уже стартовала сессия.

Для этого в начало каждой страницы вместо просто session_start() пишем:

if (isset($_REQUEST[session_name()])) session_start();

таким образом, Мы стартуем сессию только тем, кто прислал идентификатор.
Соответственно, надо еще в первый раз отправить его пользователю – в момент авторизации.

Если имя и проль верные – пишем session_start()!

Возможные проблемы и их устранение

Самыми распространенными ошибками, которые выдает РНР при попытке работать с сессиями, являются такие:
Две из них,

Warning: Cannot send session cookie — headers already sent
Warning: Cannot send session cache limiter — headers already sent

вызваны одной и той же причиной, решение описано в этом факе здесь

Третья,

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 = c:\windows\temp

И не забыть перезагрузить Apache после этого.

Как выясняется, сообразительность людская не имеет пределов, и поэтому я вынужден пояснить:
сообщение о третьей ошибке (невозможно найти каталог) НЕИЗБЕЖНО приведет к появлению первых двух, поскольку сообщение об ошибке — это вывод в браузер и после него заголовками пользоваться нельзя. Поэтому не спешите искать преждевременный вывод, а сначала пропишите правильный путь!

Следующей по распространенности проблемой при работе с сессиями является тяжелое наследие register_globals. НЕ давайте переменным скрипта имена, совпадающие с индексами массива $_SESSION!

При register_globals=on значения будут перезаписывать друг друга, и вы запутаетесь.

Если не работает, но и никаких сообщений не выводится, то добавьте в самое начало скрипта две строчки, отвечающие за вывод ВСЕХ ошибок на экран — вполне возможно, что ошибки есть, но вы их просто не видите.

ini_set(‘display_errors’,1);
error_reporting(E_ALL);

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

Если вы уверены, что ошибок нет, но приведенный пример не работает все равно, то, возможно, в PHP не включена передача ид через урл, а cookies по каким-то причинам не работают.
Смотрите, что у вас с cookies.

Вообще, если у вас «не работают» сессии, то сначала попробуйте передать идентификатор сессии руками, то есть, сделать ссылку и приписать к ней идентификатор:

session_start();
if (!isset($_SESSION[‘counter’])) $_SESSION[‘counter’]=0;
echo «Вы обновили эту страницу «.$_SESSION[‘counter’]++.» раз.

обновить»;
?>

При этом следует убедится, что не включена директива session.use_only_cookies, которая запрещает PHP принимать идентификатор сессии, если он был передан через URL

Если этот пример не заработает, то проблема либо в банальных опечатках (половина «проблем» с сессиями происходит от неправильно написанного имени переменной), либо в слишком старой версии PHP: поддержка сессий появилась в версии 4.0, а массив $_SESSION — в 4.1 (До этого использовался $HTTP_SESSION_VARS).

Если же заработает — то проблема в cookies. Отслеживайте — что за cookies ставит сервер браузеру, возвращает ли браузер ее. Искать очень полезно, просматривая просматривая обмен HTTP-заголовками между браузером и сервером.

Объяснение принципа работы cookies выходит за рамки этого и так уж слишком большого текста, но хотя бы убедитесь, что сервер cookies с идентификатором посылает, а браузер — возвращает. И при этом идентификаторы совпадают друг с другом =)
Установка cookies должна выглядеть, как

Set-Cookie: PHPSESSID=prlgdfbvlg5fbsbshch6hj0cq6;

или как

Set-Cookie: PHPSESSID=prlgdfbvlg5fbsbshch6hj0cq6; path=/

(если вы запрашиваете скрипт не из корневого каталога)
Ответ сервера должен выглядеть, как

Cookie: PHPSESSID=prlgdfbvlg5fbsbshch6hj0cq6

либо

Cookie: PHPSESSID=prlgdfbvlg5fbsbshch6hj0cq6; b=b

если браузер возвращает другие cookies, кроме идентификатора сессии.

Если пример отсюда работает, а ваш собственный код — нет, то проблема, очевидно, не в сессиях, а в алгоритме. Ищите, где потеряли переменную, по шагам переносите пример отсюда, отлаживайте свой скрипт.

Еще одна проблема может возникнуть, если вы используете перенаправление через header или навигацию с помощью JavaScript.
Дело в том, что РНР автоматически дописывает идентификатор сессии только к ссылкам вида , но не делает этого для header-ов, яваскрипта, мета-тегов.

Поэтому надо добавлять идентификатор руками, например, так:

header(«Location: /script.php?».session_name().’=’.session_id());

Так же, весьма редкая, и совершенно непонятно, откуда появляющаяся, проблема бывает в том, что настройка session.save_handler имеет значение, отличное от files. Если это не так — исправляйте.

Leave a reply

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>