Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Самоучитель по PHP 4

.pdf
Скачиваний:
82
Добавлен:
02.05.2014
Размер:
4.36 Mб
Скачать

Глава 25. Управление сессиями

351

Мы можем также создать сессию с угодным нам идентификатором, при этом автоматически установив его в 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 использовал каждый раз, когда нужно было сохранить или загрузить данные из временного хранилища. Возможно, они вас не устроят — например, вы захотите хра-

352

Часть IV. Стандартные функции 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)

Глава 25. Управление сессиями

353

Обработчик вызывается, когда сессия с идентификатором $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 нужно указать имена обработчиков, соответственно, для чтения и записи во временное хранилище. Функция с именем, заданным в

354

Часть IV. Стандартные функции PHP

$destroy, будет вызвана при уничтожении сессии. Наконец, обработчик, определяемый параметром $gc, используется как сборщик мусора.

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

Пример: переопределение обработчиков

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

Код листинга 25.2 довольно велик, но не сложен. Тут уж ничего не поделаешь — нам в любом случае приходится задавать все 6 обработчиков, а это выливается в "объемистые" описания.

Листинг 25.2. Переопределение обработчиков сессии

<?

//Возвращает полное имя файла временного хранилища сессии.

//В случае, если нужно изменить тот каталог, в котором должны

//храниться сессии, достаточно поменять только эту функцию function ses_fname($key)

{

return "sessiondata/".session_name()."/$key";

}

//Заглушки — эти функции просто ничего не делают

function ses_open($save_path, $ses_name) { return true; } function ses_close() { return true; }

// Чтение данных из временного хранилища function ses_read($key)

{

//Получаем имя файла и открываем файл $fname=ses_fname($key); $f=@fopen($fname,"rb"); if(!$f) return "";

//Читаем до конца файла

$st=fread($f,filesize($fname)); fclose($f);

return $st;

}

Глава 25. Управление сессиями

355

// Запись данных сессии во временное хранилище function ses_write($key, $val)

{

$fname=ses_fname($key);

//Сначала создаем все каталоги (в случае, если они уже есть,

//игнорируем сообщения об ошибке)

@mkdir($d=dirname(dirname($fname)),0777); @mkdir(dirname($fname),0777);

// Создаем файл и записываем в него данные сессии $f=@fopen($fname,"wb"); if(!$f) return ""; fwrite($f,$val);

fclose($f); return true;

}

//Вызывается при уничтожении сессии function ses_destroy ($key)

{

return @unlink(ses_fname($key));

}

//Сборка мусора — ищем все старые файлы и удаляем их function ses_gc($maxlifetime)

{

$dir=ses_fname(".");

//Получаем доступ к каталогу текущей группы сессии $d=@opendir($dir); if(!$d) return false;

$DelDir=1; // Признак того, что каталог пуст, и его можно удалить

//Читаем все элементы каталога

while(($e=readdir($d))!==false) {

//Если это "точки", пропускаем их if($e=="."||$e=="..") continue;

//Файл слишком старый?

if(time()-filemtime($fname="$dir/$e")>=$maxlifetime) { @unlink($fname);

continue;

}

// Нашли не очень старый файл — значит, каталог точно

356

Часть IV. Стандартные функции PHP

// не будет в результате работы пуст. $DelDir=0;

}

closedir($d);

//Если все файлы оказались слишком старые и удалены,

//удалить и каталог

if($DelDir) @rmdir($dir); return true;

}

//Регистрируем наши новые обработчики session_set_save_handler(

"ses_open", "ses_close", "ses_read", "ses_write", "ses_destroy", "ses_gc"

);

//Для примера подключаемся к группе сессий test session_name("test");

session_start(); session_register("count");

//Дальше как обычно...

$count=@$count+1; ?>

<body> <h2>Счетчик</h2>

В текущей сессии работы с браузером Вы открыли эту страницу <?=$count?> раз(а). Закройте браузер, чтобы обнулить этот счетчик. </body>

Сессии и Cookies

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

Глава 25. Управление сессиями

357

 

 

 

 

 

 

К сожалению, пользователи отключают 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>Счетчик</h2>

В текущей сессии работы с браузером Вы открыли эту страницу

358

Часть IV. Стандартные функции PHP

<?=$count?> раз(а). Закройте браузер, чтобы обнулить этот счетчик.<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

Глава 25. Управление сессиями

359

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> (правда, я сам не ожидал увидеть такой эффект, когда опробовал этот сценарий). Что же, кавычки так кавычки, хуже от этого не будет….

360

Часть IV. Стандартные функции PHP

Так использовать Cookies

в сессиях или нет?

Ответ — да, использовать. Для этого мы должны быть уверены, что в настройках PHP параметр session.use_cookies установлен в значение true (именно оно присваивается ему по умолчанию при установке PHP).

Что же делать, если пользователь отключил у себя Cookies? Да ничего. Так как PHP автоматически добавляет идентификатор сессии ко всем ссылкам и формам, которые он встретит, сценарии все равно будут продолжать работать. Вот только их URL (да и всех других документов тоже) немного удлинятся, но, думаю, это не так уж и критично. Главное, что сессии будут работать. Только не забудьте удостовериться, что в конфигурационном файле PHP включена опция session.use_trans_sid.

Итак, разработчики PHP добились, чтобы сценарию, рассчитанному на сессии, было все равно, включены Cookies в браузере пользователя, или нет. Давайте активно этим пользоваться.