|
Content:
PHP book
Sybase
Free scripts
Tutorials
ASP
Ads
Translation Service
|
Глава 25
Управление сессиями
Сессии, наконец-то появившиеся в PHP версии 4, представляют собой механизм,
позволяющий хранить некоторые (и произвольные) данные, индивидуальные
для каждого пользователя (например, его имя и номер счета), между запусками
сценария.
Термин "сессия" является транслитерацией от английского слова
session, что в буквальном переводе должно бы означать "сеанс".
Однако последнее слово в программистском жаргоне не особенно-то прижилось
(насколько я знаю), поэтому я буду употреблять термин "сессия".
И да простят меня студенты, если у них это вызывает нехорошие ассоциации.
Фактически, сессия — это некоторое место долговременной памяти (обычно
часть на жестком диске и часть — в Cookies браузера), которое сохраняет
свое состояние между вызовами сценариев одним и тем же пользователем.
Иными словами, поместив в сессию переменную (любой структуры), мы при
следующем запуске сценария получим ее в целости и сохранности. Трудно
переоценить удобства, которые это предоставляет нам, программистам.
Ca?ai io?iu nannee?
В Web-программировании есть один класс задач, который может вызвать довольно
много проблем, если писать сценарии "в лоб". Речь идет о слабой
стороне CGI — невозможности запустить программу на длительное время, позволив
ей при этом обмениваться данными с пользователями.
В общем и целом, сценарии должны запускаться, моментально выполняться
и возвращать управление системе. Теперь представьте, что мы пишем форму,
но в ней такое большое число полей, что было бы глупо поместить их на
одну страницу. Нам нужно разбить процесс заполнения формы на несколько
этапов, или стадий, и представить их в виде отдельных HTML-документов.
Это похоже на работу мастеров Windows — диалоговых окон для ввода данных
с кнопками Назад и Дальше, благодаря которым можно переместиться на шаг
в любом направлении.
Например, в первом документе с диалогом у пользователя может запрашиваться
его имя и фамилия, во втором (если первый был заполнен верно) — данные
о его месте жительства, и в третьем — номер кредитной карточки.
В любой момент можно вернуться на шаг назад, чтобы исправить те или иные
данные. Наконец, если все в порядке, накопленная информация обрабатывается
— например, помещается в базу данных.
Реализация такой схемы оказывается для Web-приложений довольно нетривиальной
проблемой. Действительно, нам придется хранить все ранее введенные данные
в каком-нибудь временном хранилище, которое должно аннулироваться, если
пользователь вдруг передумает и "уйдет" с сайта. Для этого,
как мы знаем, можно использовать функции сериализации и файлы. Однако
ими мы решаем только половину проблемы: нам нужно также как-то привязывать
конкретного пользователя к конкретному временному хранилищу. Действительно,
предположим, что мы этого не сделали. Тогда, если в момент заполнения
какой-нибудь формы одним пользователем на сайт "зайдет" другой
и тоже попытается ввести свои данные, получится куча мала.
Все эти проблемы решаются с применением сессий PHP, о которых сейчас и
пойдет речь.
Механизм работы сессий
Как же работают сессии? Для начала должен существовать механизм, который
бы позволил PHP идентифицировать каждого пользователя, запустившего сценарий.
То есть при следующем запуске PHP нужно однозначно определить, кто его
запустил: тот же человек, или другой. Делается это путем присвоения клиенту
так называемого уникального идентификатора сессии. Чтобы этот идентификатор
был доступен при каждом запуске сценария, PHP помещает его в Cookies браузера.
Использовать Cookies не обязательно, существует и другой способ. Мы поговорим
о нем чуть позже.
Теперь, зная идентификатор (дальше для краткости я буду называть его SID),
PHP может определить, в каком же файле на диске хранятся данные пользователя.
Немного о том, как сохранять переменную (обязательно глобальную) в сессии.
Для этого мы должны ее зарегистрировать с помощью специальной функции.
После регистрации мы можем быть уверены, что при следующем запуске сценария
тем же пользователем она получит то же самое значение, которое было у
нее при предыдущем завершении программы. Это произойдет потому, что при
завершении сценария PHP автоматически сохраняет все переменные, зарегистрированные
в сессии, во временном хранилище. Конечно, можно в любой момент аннулировать
переменную — "вычеркнуть" ее из сессии, или же уничтожить вообще
все данные сессии.
Где же находится то промежуточное хранилище, которое использует PHP? Вообще
говоря, вы вольны сами это задать, написав соответствующие функции и зарегистрировав
их как обработчики сессии. Впрочем, делать это не обязательно: в PHP уже
существуют обработчики по умолчанию, которые хранят данные в файлах (в
системах Unix для этого обычно используется директория /tmp). Если вы
не собираетесь создавать что-то особенное, вам они вполне подойдут.
Eieoeaeecaoey nannee
Но прежде, чем работать с сессией, ее необходимо инициализировать. Делается
это путем вызова специальной функции session_start().
Если вы поставили в настройках PHP режим session.auto_start=1, то функция
инициализации вызывается автоматически при запуске сценария. Однако, как
мы вскоре увидим, это лишает нас множества полезных возможностей (например,
не позволяет выбирать свою, особенную, группу сессий). Так что лучше не
искушать судьбу и вызывать session_start() a первой строчке вашей программы.
Следите также за тем, чтобы до нее не было никакого вывода в браузер —
иначе PHP не сможет установить SID для пользователя!
void session_start()
Эта функция инициализирует механизм сессий для текущего пользователя,
запустившего сценарий. По ходу инициализации она выполняет ряд действий.
? Если посетитель запускает программу впервые, у него устанавливается
Cookies с уникальным идентификатором, и создается временное хранилище,
ассоциированное с этим идентификатором.
? Определяется, какое хранилище связано с текущим идентификатором пользователя.
? Если в хранилище имеются какие-то переменные, их значения восстанавливаются.
Точнее, создаются глобальные переменные, которые были сохранены в сессии
при предыдущем завершении сценария.
Вообще говоря, рассмотренный механизм, как всегда, не совсем точно соответствует
истинному положению вещей. А именно, все зависит от того, какое значение
присвоено настроечному параметру register_globals. Aсли register_globals=0,
то в сессии можно будет сохранять (a потом и восстанавливать) только величины,
содержащиеся в глобальном ассоциативном массиве $HTTP_SESSION_VARS. Если
же этот параметр содержит значение "истина" (как обычно и происходит
по умолчанию), то в сессии можно регистрировать глобальные переменные.
Регистрация переменных
bool session_register(mixed $name [, mixed $name1, ...])
PHP узнает о том, что ту или иную переменную нужно сохранить в сессии,
если ее предварительно зарегистрировать. Для этого и предназначена функция
session_register(). Она принимает в параметрах одно или несколько имен
переменных (имена задаются в строках, без знака $ слева), регистрирует
их в текущей запущенной сессии и возвращает значение "истина",
если все прошло корректно.
Почему же тогда я описал типы параметров как mixed, а не как string? Да
потому, что на самом деле в функцию можно передавать не одну строку в
каждом параметре, а сразу список строк. Каждая такая строка будет регистрировать
отдельную переменную с соответствующим именем. Более того — элементом
списка может опять же быть список строк, и т. д.
Нет ничего страшного, если мы дважды зарегистрируем одну и ту же переменную
в сессии. На самом деле, чаще всего как раз так и происходит — при повторном
запуске сценария. Вот пример:
Листинг 25.1. Пример работы с сессиями
<?
session_start();
session_register("count");
$count=@$count+1;
?>
<body>
<h2>Счетчик</h2>
В текущей сессии работы с браузером Вы открыли эту страницу
<?=$count?> раз(а). Закройте браузер, чтобы обнулить счетчик.
</body>
Как видим, все предельно просто.
Идентификатор сессии
и имя группы
Что же, теперь мы уже можем начать писать кое-какие сценарии. Но вскоре
возникнет небольшая проблема. Дело в том, что на одном и том же сайте
могут сосуществовать сразу несколько сценариев, которые нуждаются в услугах
поддержки сессий PHP. Они "ничего не знают" друг о друге, поэтому
временные хранилища для сессий должны выбираться не только на основе идентификатора
пользователя, но и на основе того, какой из сценариев запросил обслуживание
сессии.
Имя группы сессий
Что, не совсем понятно? Хорошо, тогда рассмотрим пример. Пусть разработчик
A написал сценарий счетчика, приведенный в листинге 25.1. Он использует
переменную $count, и не имеет никаких проблем. До тех пор, пока разработчик
B, ничего не знающий о сценарии A, не создал систему статистики, которая
тоже использует сессии. Самое ужасное, что он также регистрирует переменную
$count, не зная о том, что она уже "занята". В результате, как
всегда, страдает пользователь: запустив сначала сценарий разработчика
B, а потом — A, он видит, что данные счетчиков перемешались. Непорядок!
Нам нужно как-то разграничить сессии, принадлежащие одному сценарию, от
сессий, принадлежащих другому. К счастью, разработчики PHP предусмотрели
такое положение вещей. Мы можем давать группам сессий непересекающиеся
имена, и сценарий, знающий имя своей группы сессии, сможет получить к
ней доступ. Вот теперь-то разработчики A и B могут оградить свои сценарии
от проблем с пересечениями имен переменных. Достаточно в первой программе
указать PHP, что мы хотим использовать группу с именем, скажем, sesA,
а во второй — sesB.
string session_name([string $newname])
Эта функция устанавливает или возвращает имя группы сессии, которая будет
использоваться PHP для хранения зарегистрированных переменных. Если $newname
не задан, то возвращается текущее имя. Если же этот параметр указан, то
имя группы будет изменено на $newname, при этом функция вернет предыдущее
имя.
Session_name() лишь сменяет имя текущей группы и сессии, но не создает
новую сессию и временное хранилище! Это значит, что мы должны в большинстве
случаев вызывать session_name(имя_группы) еще до ее инициализации — вызова
session_start(), в противном случае мы получим совсем не то, что ожидали.
Если функция session_name() не была вызвана до инициализации, PHP будет
использовать имя по умолчанию — PHPSESID.
Кстати говоря, имя группы сессий, устанавливаемое рассматриваемой функцией,
— это как раз имя того самого Cookie, который посылается в браузер клиента
для его идентификации. Таким образом, пользователь может одновременно
активизировать две и более сессий — с точки зрения PHP он будет выглядеть
как два ли более различных пользователя. Однако не забывайте, что, случайно
установив в сценарии Cookie, имя которого совпадает с одним из имен группы
сессий, вы "затрете" Cookie.
Вот простой пример применения этой функции.
<?
session_name("CounterScript"
session_start();
session_register("count");
$count=@$count+1;
?>
В текущей сессии Вы открыли эту страницу <?=$count?> раз(а).
Рекомендую всегда указывать имя группы сессии вручную, не полагаясь на
значение по умолчанию. За это вам скажут спасибо разработчики других сценариев,
когда они захотят использовать вашу программу вместе со своими.
Идентификатор сессии
Мы уже говорили с вами, зачем нужен идентификатор сессии (SID). Фактически,
он является именем временного хранилища, которое будет использовано для
хранения данных сессии между запусками сценария. Итак, один SID — одно
хранилище. Нет SID, нет и хранилища, и наоборот.
В этом месте очень легко запутаться. В самом деле, как же соотносится
идентификатор сессии и имя группы? А вот как: имя — это всего лишь собирательное
название для нескольких сессий (то есть, для многих SID), запущенных разными
пользователями. Один и тот же клиент никогда не будет иметь два различных
SID в пределах одного имени группы. Но его браузер вполне может работать
(и часто работает) с несколькими SID, расположенными логически в разных
"пространствах имен".
Итак, все SID уникальны и однозначно определяют сессию на компьютере,
выполняющем сценарий — независимо от имени сессии. Имя же задает "пространство
имен", в которое будут сгруппированы сессии, запущенные разными пользователями.
Один клиент может иметь сразу несколько активных пространств имен (то
есть несколько имен групп сессий).
string session_id([string $sid])
Функция возвращает текущий идентификатор сессии SID. Если задан параметр
$sid, то у активной сессии изменяется идентификатор на $sid. Делать это,
вообще говоря, не рекомендуется.
Фактически, вызвав session_id() до session_start(), мы можем подключиться
к любой (в том числе и к "чужой") сессии на сервере, если знаем
ее идентификатор. Мы можем также создать сессию с угодным нам идентификатором,
при этом автоматически установив его в Cookies пользователя. Но это —
не лучшее решение, — предпочтительнее переложить всю "грязную работу"
на PHP.
Другие функции
Здесь мы для полноты картины рассмотрим функции для работы с сессиями,
которые применяются гораздо реже, чем уже описанные.
bool session_is_registered(string $name)
Функция session_is_registered() возвращает значение true, если переменная
с именем $name была зарегистрирована в сессии, иначе возвращается false.
bool session_unregister(struing $name)
Эта функция отменяет регистрацию для переменной с именем $name для текущей
сессии. Иными словами, при завершении сценария все будет выглядеть так,
словно переменная с именем $name и не была никогда зарегистрирована. Возвращает
true, если все прошло успешно, и false — в противном случае.
После вызова функции session_unregister() глобальная переменная, которая
была "аннулирована", не уничтожается, а сохраняет свое значение.
void session_unset()
Функция session_unset(), в отличие от session_unregister(), не только
отменяет регистрацию переменных (кстати говоря, всех переменных сессии,
а не какой-то одной), но и уничтожает глобальные переменные, которые были
зарегистрированы в сессии.
string session_save_path([string $path])
Эта функция возвращает имя каталога, в котором будут помещаться файлы
— временные хранилища данных сессии. В случае, если указан параметр, как
обычно, активное имя каталога будет переустановлено на $path. При этом
функция вернет предыдущий каталог.
К сожалению, функции, которая бы возвращала список всех зарегистрированных
в сессии переменных, почему-то нет. Во всяком случае, в PHP версии 4.0.3.
Установка обработчиков сессии
До сих пор мы с вами пользовались стандартными обработчиками сессии, которые
PHP использовал каждый раз, когда нужно было сохранить или загрузить данные
из временного хранилища. Возможно, они вас не устроят — например, вы захотите
хранить переменные сессии в базе данных или еще где-то. В этом случае
достаточно будет переопределить обработчики своими собственными функциями,
и вот как оно делается.
Обзор обработчиков
Всего существует 6 функций, связанных с сессиями, которые PHP вызывает
в тот или иной момент работы механизма обработки сессий. Им передаются
различные параметры, необходимые для работы. Сейчас я перечислю все эти
функции вместе с их описаниями.
bool handler_open(string $save_path, string $session_name)
Функция вызывается, когда вызывается session_start(). Обработчик должен
взять на себя всю работу, связанную с открытием базы данных для группы
сессий с именем $session_name. В параметре $save_path передается то, что
было указано при вызове session_save_path() или же путь к файлам-хранилищам
данных сессий по умолчанию. Возможно, если вы используете базу данных,
этот параметр будет бесполезным.
bool handler_close()
Этот обработчик вызывается, когда данные сессии уже записаны во временное
хранилище и его нужно закрыть.
string handler_read(string $sid)
Вызов обработчика происходит, когда нужно прочитать данные сессии с идентификатором
$sid из временного хранилища. Функция должна возвращать данные сессии
в специальном формате, который выглядит так:
имя1=значение1;имя2=значение2;имя3=значение3;...;
Здесь имяN задает имя очередной переменной, зарегистрированной в сессии,
а значениеN — результат вызова функции Serialize() для значения этой переменной.
Например, запись может иметь следующий вид:
foo|i:1;count|i:10;
Она говорит о том, что из временного хранилища были прочитаны две целые
переменные, первая из которых равна 1, а вторая — 10.
string handler_write(string $sid, string $data)
Этот обработчик предназначен для записи данных сессии с идентификатором
$sid во временное хранилище — например, открытое ранее обработчиком handler_open().
Параметр $data задается в точно таком же формате, который был описан выше.
Фактически, чаще всего действия этой функции сводятся к записи в базу
данных строки $data без каких-либо ее изменений.
bool handler_destroy(string $sid)
Обработчик вызывается, когда сессия с идентификатором $sid должна быть
уничтожена.
bool handler_gc(int $maxlifetime)
Данный обработчик — особенный. Он вызывается каждый раз при завершении
работы сценария. Если пользователь окончательно "покинул" сервер,
значит, данные сессии во временном хранилище можно уничтожить. Этим и
должна заниматься функция handler_gc(). Ей передается в параметрах то
время (в секундах), по прошествии которого PHP принимает решение о необходимости
"почистить перышки", или "собрать мусор" (garbage
collection) — т. е., это максимальное время существования сессии.
Как же должна работать рассматриваемая функция? Очень просто. Например,
если мы храним данные сессии в базе данных, мы просто должны удалить из
нее все записи, доступ к которым не осуществлялся более, чем $maxlifetime
секунд. Таким образом, "застарелые" временные хранилища будут
иногда очищаться.
На самом деле обработчик handler_gc() вызывается не при каждом запуске
сценария, а только изредка. Когда именно — определяется конфигурационным
параметром session.gc_probability. А именно, им задается (в процентах),
какова вероятность того, что при очередном запуске сценария будет выбран
обработчик "чистки мусора". Сделано это для улучшения производительности
сервера, потому что обычно сборка мусора — довольно ресурсоемкая задача,
особенно если сессий много.
Регистрация обработчиков
Вы, наверное, обратили внимание, что при описании обработчиков я указывал
их имена с префиксом handler. На самом деле, это совсем не является обязательным.
Даже наоборот — вы можете давать такие имена своим обработчикам, какие
только захотите.
Но возникает вопрос: как же тогда PHP их найдет? Вот для этого и существует
функция регистрации обработчиков, которая говорит интерпретатору, какую
функцию он должен вызывать при наступлении того или иного события.
void session_set_save_handler($open,$close,$read,$write,$destroy,$gc)
Эта функция регистрирует подпрограммы, имена которых переданы в ее параметрах,
как обработчики текущей сессии. Параметр $open содержит имя функции, которая
будет вызвана при инициализации сессии, а $close — функции, вызываемой
при ее закрытии. В $read и $write нужно указать имена обработчиков, соответственно,
для чтения и записи во временное хранилище. Функция с именем, заданным
в $destroy, будет вызвана при уничтожении сессии. Наконец, обработчик,
определяемый параметром $gc, используется как сборщик мусора.
Эту функцию можно вызывать только до инициализации сессии, в противном
случае она просто игнорируется.
Пример: переопределение обработчиков
Давайте напишем пример, который бы иллюстрировал механизм переопределения
обработчиков. Мы будем держать временные хранилища сессий в подкаталоге
sessiondata текущего каталога, и для каждого имени группы сессий создавать
отдельный каталог.
Код листинга 25.2 довольно велик, но не сложен. Тут уж ничего не поделаешь
— нам в любом случае приходится задавать все 6 обработчиков, а это выливается
в "объемистые" описания.
Листинг 25.2. Переопределение обработчиков сессии
<?
// Aica?auaao iieiia eiy oaeea a?aiaiiiai o?aieeeua nannee.
// A neo?aa, anee io?ii eciaieou oот каталог, a eioi?iм aie?iu
// o?aieouny nannee, ainoaoi?ii iiiaiyou oieuei yoo ooieoe?
function ses_fname($key)
{
return "sessiondata/".session_name()."/$key";
}
// Caaeooee — yoe ooieoee i?inoi ie?aai ia aaea?o
function ses_open($save_path, $ses_name) { return true; }
function ses_close() { return true; }
// ?oaiea aaiiuo ec a?aiaiiiai o?aieeeua
function ses_read($key)
{
// Iieo?aai eiy oaeea e ioe?uaaai файл
$fname=ses_fname($key);
$f=@fopen($fname,"rb"); if(!$f) return "";
// ?eoaai ai eiioa oaeea
$st=fread($f,filesize($fname));
fclose($f);
return $st;
}
// Caienu aaiiuo nannee ai a?aiaiiia o?aieeeua
function ses_write($key, $val)
{
$fname=ses_fname($key);
// Nia?aea nicaaai ana каталоги (a neo?aa, anee iie o?a anou,
// eaii?e?oai niiauaiey ia ioeaea)
@mkdir($d=dirname(dirname($fname)),0777);
@mkdir(dirname($fname),0777);
// Nicaaai oaee e caienuaaai a iaai aaiiua nannee
$f=@fopen($fname,"wb"); if(!$f) return "";
fwrite($f,$val);
fclose($f);
return true;
}
// Aucuaaaony i?e oie?oi?aiee nannee
function ses_destroy ($key)
{
return @unlink(ses_fname($key));
}
// Nai?ea ioni?a — euai ana noa?ua oaeeu e oaaeyai eo
function ses_gc($maxlifetime)
{
$dir=ses_fname(".");
// Iieo?aai ainooi e каталогу oaeouae a?oiiu nannee
$d=@opendir($dir); if(!$d) return false;
$DelDir=1; // I?eciae oiai, ?oi каталог iono, e его ii?ii oaaeeou
// ?eoaai ana yeaiaiou eaoaeiaa
while(($e=readdir($d))!==false) {
// Anee yoi "oi?ee", i?iioneaai eo
if($e=="."||$e=="..") continue;
// Oaee neeoeii noa?ue?
if(time()-filemtime($fname="$dir/$e")>=$maxlifetime) {
@unlink($fname);
continue;
}
// Iaoee ia i?aiu noa?ue oaee — cia?eo, каталог oi?ii
// ia aoaao a ?acoeuoaoa ?aaiou iono.
$DelDir=0;
}
closedir($d);
// Anee ana oaeeu ieacaeenu neeoeii noa?ua e oaaeены,
// oaaeeou e каталог
if($DelDir) @rmdir($dir);
return true;
}
// ?aaeno?e?oai iaoe iiaua ia?aaio?eee
session_set_save_handler(
"ses_open", "ses_close",
"ses_read", "ses_write",
"ses_destroy", "ses_gc"
);
// Aey i?eia?a iiaee??aainy e a?oiia nannee test
session_name("test");
session_start();
session_register("count");
// Aaeuoa eae iau?ii...
$count=@$count+1;
?>
<body>
<h2>N?ao?ee</h2>
A oaeouae nannee ?aaiou n a?aoca?ii Au ioe?uee yoo no?aieoo
<?=$count?> ?ac(a). Cae?ieoa a?aoca?, ?oiau iaioeeou yoio n?ao?ee.
</body>
Сессии и Cookies
До сих пор я подразумевал, что использование сессий немыслимо без Cookies.
Действительно, Cookies представляют собой наиболее элегантное и простое
решение задачи идентификации каждого подключившегося пользователя, что
необходимо для связи временного хранилища и данных сессии. Но как быть,
если пользователи отключили Cookies в своих браузерах?
К сожалению, пользователи отключают Cookies гораздо чаще, чем это может
показаться на первый взгляд. Например, всего год назад Всероссийский Клуб
Вебмастеров проводил опрос, в результате которого выяснилось, что количество
пользователей Интернета, отключивших у себя по каким-то соображениям поддержку
Cookies, достигает 20—30%. Что это за соображения? Многие думают, что
Cookies потенциально являются "дырой" в безопасности их компьютера.
Это совершенно не соответствует действительности, потому что браузеры
всегда имеют ограничения на количество и суммарный объем Cookies, которые
могут быть в них установлены. Другие же просто не хотят, чтобы неизвестно
кто писал что угодно на их жесткий диск. Правда, это не мешает таким "перестраховщикам"
открывать пришедший по почте исполняемый файл — такой же, как из письма
типа "Love letter"…
В общем, вы видите, что для абсолютной уверенности в работоспособности
ваших сценариев на любом браузере нужен механизм, позволяющий отказаться
от использования Cookies при управлении сессиями. Такой механизм действительно
существует в PHP, и основная его идея состоит в том, чтобы передавать
идентификатор сессии не в Cookies, а каким-нибудь аналогичным путем —
например, в данных запроса GET. Последнее мы сейчас и рассмотрим.
Явное использование
константы SID
В PHP существует одна специальная константа с именем SID. Она всегда содержит
имя группы текущей сессии и ее идентификатор в формате имя=идентификатор.
Вспомните: именно в таком формате данные принимаются, когда они приходят
из Cookies браузера. Таким образом, нам достаточно просто-напросто передать
значение константы SID в сценарий, чтобы он "подумал", будто
бы данные пришли из Cookies. Вот пример:
Листинг 25.3. Sesget.php: простой пример использования сессий без Cookies
<?
session_name("test");
session_start();
session_register("count");
$count=@$count+1;
?>
<body>
<h2>N?ao?ee</h2>
A oaeouae nannee ?aaiou n a?aoca?ii Au ioe?uee yoo no?aieoo
<?=$count?> ?ac(a). Cae?ieoa a?aoca?, ?oiau iaioeeou yoio n?ao?ee.<hr>
<a href=sesget.php?<?=SID?>>Click here!</a>
</body>
Если набрать в браузере адрес вроде такого:
http://www.somehost.ru/sesget.php
то создастся новая сессия с уникальным идентификатором. Разумеется, если
сразу же нажать кнопку Обновить, счетчик не увеличится, потому что при
каждом запуске будет создаваться новое временное хранилище — у PHP просто
нет информации об идентификаторе пользователя. Теперь обратите внимание
на предпоследнюю строчку листинга 25.3. Видите, как хитро мы передаем
в сценарий, запускаемый через гиперссылку, данные об идентификаторе текущей
сессии? Теперь с его точки зрения они якобы пришли из Cookies…
Все будет работать так, как описано, только в том случае, если в браузере
действительно отключены Cookies. Если же они включены, PHP просто не будет
генерировать константу SID (она будет пустой) и задействует Cookies. Все
вполне логично.
Неявное изменение гиперссылок
Похоже, что вы уже начали думать о том, как же это все-таки неудобно —
везде вставлять участки кода <?=SID?>, и, пропусти вы их в одном
месте, придется долго искать ошибку? Что же, законный повод для беспокойства,
но, к счастью, разработчики PHP уберегли нас и от этой напасти.
Вы не поверите, но, если в какой-нибудь гипессылке вы по ошибке пропустите
<?=SID?>, PHP вставит его за вас автоматически. Причем так, чтобы
это никак не повредило другим параметрам, возможно, уже присутствующим
в URL. Если вы в шоке, то запустите следующий сценарий в браузере, а затем
наведите мышь на гиперссылку и посмотрите в строке состояния, какой адрес
имеет ссылка:
<?session_start()?>
<body>
<a href=/path/to/something.php>Click here!</a><br>
<a href=/path/to/something.html?a=aaa&b=bbb>Click here!</a><br>
<a href=/>Click here!</a><br>
</body>
Вот адреса этих ссылок с точки зрения браузера:
http://www.somehost.ru/path/to/something.php?PHPSESSID=8114536a920bfb01f
http://www.somehost.ru/path/to/something.html?a=aaa&b=bbb&PHPSESSID=86a20
http://www.somehost.ru/?PHPSESSID=8114536a920bfb2a
(Я немного урезал идентификаторы сессий, чтобы они уместились на странице
этой книги.) Обратите внимание на второй адрес: он говорит, что идентификатор
корректно вставился в конец обычных параметров страницы. Третий пример
заставляет задуматься о том, что идентификатор сессии прикрепляется к
URL независимо от типа документа, на который он указывает.
Описанная только что возможность работает лишь в том случае, если в настройках
PHP установлен в значение истина параметр session.use_trans_sid.
Он как раз и включен по умолчанию.
Зачем же тогда нужна константа SID? Да незачем. Это — устаревший прием
передачи идентификатора сессии, и я привел его здесь только для того,
чтобы нарисовать более полную картину, что в действительности происходит,
а также показать, насколько иногда PHP может быть услужлив.
Неявное изменение формы
Возможно, прочитав этот заголовок, вы еще более обрадуетесь. Да, PHP умеет
не только изменять гиперссылки, он также и добавляет скрытые поля в формы,
которые формирует сценарий, чтобы передать идентификатор сессии вызываемому
документу! Это ставит последнюю точку над i в вопросе поддержки сессий
для пользователей, которые отключили у себя Cookies.
Напоследок рассмотрим пример сценария, который выводит обыкновенную пустую
форму, и в ней, как по мановению волшебной палочки, появляется дополнительное
скрытое поле с идентификатором сессии.
<?session_start()?>
<form action=aaa method=post>
</form>
А вот почти дословно то, что выдается в браузере (Internet Explorer) после
запуска этого сценария и выбора в меню пункта Просмотр в виде HTML:
<form action="aaa" method="post">
<INPUT TYPE=HIDDEN NAME="PHPSESSID" VALUE="0a717e848e91db11b524a">
</form>
Как видим, PHP добавил в форму скрытое поле с нужным именем и значением.
Он также заключил в кавычки значения атрибутов тэга <form> (правда,
я сам не ожидал увидеть такой эффект, когда опробовал этот сценарий).
Что же, кавычки так кавычки, хуже от этого не будет….
Так использовать Cookies
a nanneyo или нет?
Ответ — да, использовать. Для этого мы должны быть уверены, что в настройках
PHP параметр session.use_cookies установлен в значение true (именно оно
присваивается ему по умолчанию при установке PHP).
Что же делать, если пользователь отключил у себя Cookies? Да ничего. Так
как PHP автоматически добавляет идентификатор сессии ко всем ссылкам и
формам, которые он встретит, сценарии все равно будут продолжать работать.
Вот только их URL (да и всех других документов тоже) немного удлинятся,
но, думаю, это не так уж и критично. Главное, что сессии будут работать.
Только не забудьте удостовериться, что в конфигурационном файле PHP включена
опция session.use_trans_sid.
Итак, разработчики PHP добились, чтобы сценарию, рассчитанному на сессии,
было все равно, включены Cookies в браузере пользователя, или нет. Давайте
активно этим пользоваться.
|