Лекция
Привет, сегодня поговорим про сессии в php, обещаю рассказать все что знаю. Для того чтобы лучше понимать что такое сессии в php, связь с cookies , настоятельно рекомендую прочитать все из категории Выполнение скриптов на стороне сервера PHP (LAMP) NodeJS (Backend) .
Подробное описание работы и объяснение механизма работы с сессиями PHP.
Надо только понимать, для чего сессии нужны и как устроены. Ответим сначала на первый вопрос.
Возможно Вы знаете, что веб-сервер не поддерживает постоянного соединения с клиентом, и каждый запрос обрабатывается, как новый, без связи с предыдущими.
То есть, нельзя ни отследить запросы от одного и того же посетителя, ни сохранить для него переменные между просмотрами отдельных страниц. Вот для решения этих двух задач и были изобретены сессии.
Собственно, сессии, если в двух словах - это механизм, позволяющий однозначно идентифицировать браузер и создающий для этого браузера файл на сервере, в котором хранятся переменные сеанса.
Подробно расписывать нужду в таком механизме я не буду. Это такие случаи, как корзина покупок в интернет магазине, авторизация, а так же, и не совсем тривиальные проблемы, такие, например, как защита интерактивных частей сайта от спама.
В принципе, довольно несложно сделать собственный аналог сессий, не такой функциональный, как встроенный в PHP, но похожий по сути. На cookies и базе данных.
При запросе скрипта смотрим, пришла ли cookies с определенным именем. Если cookies нет, то ставим ее и записываем в базу новую строку с данными пользователя. Если cookies есть, то читаем из базы данные. Еще одним запросом удаляем из базы старые записи и вот у нас готов механизм сессий. Совсем несложно. Но есть некоторые нюансы, которые делают предпочтительным использование именно встроенного механизма сессий.
Для начала надо как-то идентифицировать браузер. Для этого надо выдать ему уникальный идентификатор и попросить передавать его с каждым запросом. Стыдно признаться, но когда я впервые узнал о сессиях, я думал, что это какой-то особый механизм, некий новый способ общения браузера с сервером - "сессии". Что идентификатор сессии передается каким-то особым образом. Но, разочарование было жестоким...
Сессии используют стандартные, хорошо известные способы передачи данных. Собственно, других-то просто и нет.
Идентификатор - это обычная переменная. По умолчанию ее имя - 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 просматривает ее всю и дописывает к каждой ссылке и к каждой форме передачу идентификатора сессии. Это выглядит примерно так:
превращается в
а к формам добавляется скрытое поле
И браузер при клике на любую ссылку, или при нажатии на кнопку в форме, пошлет в запросе нужную нам переменную - идентификатор сессии!
Теоретически, в наших с вами самодельных сессиях на 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 тоже занимается сам. Как и кодированием данных и кучей всяких других нужных вещей. В результате этой заботы работа с сессиями оказывается очень простой.
Вот мы, собственно, и подошли к примеру работы сессий.
Пример очень маленький:
Мы проверяем, есть ли у нас в сессии переменная counter, если нет, то создаем ее со значением 0, а дальше выводим ее значение и увеличиваем на единицу. Увеличенное значение запишется в сессию, и при следующем вызове скрипта переменная будет иметь значение 1, и так далее. Все очень просто.
Для того, чтобы иметь доступ к переменным сессии на любых страницах сайта, надо написать ТОЛЬКО ОДНУ(!) строчку в самом начале КАЖДОГО файла, в котором нам нужны сессии:
session_start();
И далее обращаться к элементам массива $_SESSION. Об этом говорит сайт https://intellect.icu . Например, проверка авторизации будет выглядеть примерно так:
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.use_only_cookies
, которая запрещает PHP принимать идентификатор сессии, если он был передан через URL
Если этот пример не заработает, то проблема либо в банальных опечатках (половина "проблем" с сессиями происходит от неправильно написанного имени переменной), либо в слишком старой версии PHP: поддержка сессий появилась в версии 4.0, а массив $_SESSION
- в 4.1 (До этого использовался $HTTP_SESSION_VARS
).
Если же заработает - то проблема в cookies. Отслеживайте - что за cookies ставит сервер браузеру, возвращает ли браузер ее. Искать очень полезно, просматривая просматривая обмен HTTP-заголовками между браузером и сервером.
Объяснение принципа работы cookies выходит за рамки этого и так уж слишком большого текста, но хотя бы убедитесь, что сервер cookies с идентификатором посылает, а браузер - возвращает. И при этом идентификаторы совпадают друг с другом =)
Установка cookies должна выглядеть, как
Set-Cookie: PHPSESSID=prgjyjugjygh6hj0cq6;
или как
Set-Cookie: PHPSESSID=prlgdfkjuyuiyiuhch6hj0cq6; path=/
(если вы запрашиваете скрипт не из корневого каталога)
Ответ сервера должен выглядеть, как
Cookie: PHPSESSID=prlgdfbioulyui6hj0cq6
либо
Cookie: PHPSESSID=prlgdfbkjhgjk6hj0cq6; b=b
если браузер возвращает другие cookies, кроме идентификатора сессии.
Если пример отсюда работает, а ваш собственный код - нет, то проблема, очевидно, не в сессиях, а в алгоритме. Ищите, где потеряли переменную, по шагам переносите пример отсюда, отлаживайте свой скрипт.
Еще одна проблема может возникнуть, если вы используете перенаправление через header или навигацию с помощью JavaScript.
Дело в том, что РНР автоматически дописывает идентификатор сессии только к ссылкам вида , но не делает этого для header-ов, яваскрипта, мета-тегов.
Поэтому надо добавлять идентификатор руками, например, так:
header("Location: /script.php?".session_name().'='.session_id());
Так же, весьма редкая, и совершенно непонятно, откуда появляющаяся, проблема бывает в том, что настройка session.save_handler имеет значение, отличное от files. Если это не так - исправляйте.
Если вы пытались сделать несколько одновременных AJAX-запросов (например, с помощью jQuery) к PHP-скрипту, то могли столкнуться с тем, что на самом деле эти запросы не обрабатывались одновременно. Вместо этого запросы выполнялись последовательно и, таким образом, блокировали друг друга. Ограничение HTTP-соединений в браузере, вероятно, не является причиной, поскольку большинство браузеров имеют ограничение не менее шести одновременных HTTP-соединений .
Более вероятное объяснение состоит в том, что ваш сценарий либо записывает, либо читает данные сеанса. По умолчанию данные сеанса в PHP хранятся в файлах файловой системы. Обычно запись в файл происходит после завершения сценария, и для предотвращения одновременных записей на файл сеанса устанавливается монопольная блокировка. Это означает, что в любой момент времени только один сценарий (т. е. процесс или поток) может работать с данными сеанса. В результате другие параллельные запросы просто ждут освобождения блокировки, чтобы можно было продолжить выполнение — по одному за раз. Это приводит к тому, что выполнение будет последовательным, а не параллельным, как ожидалось.
К счастью, PHP предоставляет session_write_close , который можно использовать для решения этой проблемы. Что делает эта функция, так это записывает данные сеанса в файл и освобождает блокировку, которая затем позволяет другим запросам получить к ней доступ. Поэтому, как только все необходимые данные будут сохранены в сессию, следует вызвать эту функцию. В этот момент запрос перестанет блокировать другие параллельные запросы для того же скрипта. После вызова функции по-прежнему можно будет читать данные сеанса, но запись невозможна.
Приведенный ниже код является примером сценария, в котором функция session_write_close может использоваться для увеличения пропускной способности скрипта.
Хотя приведенный выше пример является чисто теоретическим, представьте, что к сценарию выполняются пять одновременных запросов AJAX. Каждое выполнение длится примерно десять секунд. Это означает, что каждый запрос будет блокировать последующий запрос примерно на десять секунд. Это происходит только потому, что осуществляется доступ к данным сеанса, и, таким образом, блокируется файл сеанса. Как вы можете себе представить, в этой блокировке нет необходимости, потому что данные сеанса изменяются только перед выполнением трудоемкой части скрипта. Таким образом, можно закрыть сеанс сразу после изменения данных, чтобы сэкономить время, потому что тогда запрос больше не будет блокировать другие запросы. Этот подход отражен во фрагменте кода ниже.
Вы по-прежнему сможете читать данные сеанса после закрытия сеанса, поэтому вам нужно быть осторожным только при изменении данных сеанса. После введения этого простого вызова функции вы должны заметить, что ваши запросы теперь выполняются параллельно и больше не блокируют друг друга (после вызова функции). Это потенциально может значительно повысить производительность вашего приложения, в зависимости от варианта использования.
Дополнительная информация:
Надеюсь, эта статья про сессии в php, была вам полезна, счастья и удачи в ваших начинаниях! Надеюсь, что теперь ты понял что такое сессии в php, связь с cookies и для чего все это нужно, а если не понял, или есть замечания, то не стесняйся, пиши или спрашивай в комментариях, с удовольствием отвечу. Для того чтобы глубже понять настоятельно рекомендую изучить всю информацию из категории Выполнение скриптов на стороне сервера PHP (LAMP) NodeJS (Backend)
Комментарии
Оставить комментарий
Выполнение скриптов на стороне сервера PHP (LAMP) NodeJS (Backend)
Термины: Выполнение скриптов на стороне сервера PHP (LAMP) NodeJS (Backend)