Обзор механизма сессий PHP4

Denis Kolisnichenko

Эта статья предназначена для новичков, которые еще не знакомы с этим замечательным механизмом, существенно облегчающим жизнь Web-программистам. Интерпретатор PHP4 можно скачать на сайте http://www.php.net. Я использую версию 4.1.1, но даже, если у вас версия 4.0.0 все примеры из этой статьи будут работать.
Одним из недостатков интерфейса CGI является то, что любая CGI-программане может длительное время "общаться" с пользователем: она должна запуститься, обработать переданные ей параметры и выдать какой-нибудь результат. Представьте себе, что вам нужно написать некоторую анкету, но полей в ней так много, что они не умещаются в одном окне броузера (а из-за этого, как всегда, страдает дизайн) или нужно обработать некоторые данные перед вводом следующих. Эту проблему можно решить путем использования временного файла. Но, предположим, что в один момент с анкетой работают два или более пользователей, тогда вариант с файлов отпадает сам по себе. Тогда вам нужно будет использовать базу данных, но стоит ли использовать целую базу данных для хранения промежуточных данных? Более продвинутые станут использовать Cookies. Это отличное решение, но PHP4 предоставляет более гибкое решение этой проблемы - механизм сессий. К тому же, у пользователя, зашедшего на ваш сайт, Cookies могут быть отключены. Механизм сессий по умолчанию используется Cookies, но если они отключены, ваши сценарии, использующие сессии, все равно будут работать. О том, как это достигается, поговорим немного позже.
Для идентификации пользователя используется идентификатор сессии - SID. Именно с его помощью можно определить, какой пользователь запустил сценарий. Идентификатор сессии хранится в Cookies броузера - вот для чего нужны Cookies! Фактически, SID - это имя временного хранилища, то есть имя временного файла, в котором PHP4 хранит информацию о сессии. Обычно эти файлы размещаются в каталоге /tmp.

Рассмотрим самый простой пример использования сессий (см. листинг 1).


<?
session_name("SessionOfIvan");
session_start();
session_register("a");
$a=@$a+1;
echo "<html><body>Нажмите Reload, чтобы увеличить счетчик";
echo "<br>Счетчик: $a";
echo "</body></html>";

?>
Функция session_start() инициализирует сессию, а функция session_register("a") регистрирует в сессии переменную $a. Имена переменных задаются без знака доллара. Можно несколько раз регистрировать одну и ту же переменную в сессии (что я и сделал в листинге 1) - ее значение не обнулится при повторной регистрации. Если запустить наш сценарий и несколько раз нажать кнопку броузера Reload, счетчик будет увеличиваться, а если закрыть окно броузера, счетчик обнулится. Следует также отметить, что сценарии, которые одновременно выполняются, ничего не знают друг о друге. Другими словами, если вы запустите сценарий и пару раз нажмете на кнопку Reload для изменения счетчика, а потом, не закрывая окна броузера, запустите броузер снова и запустите еще раз этот же сценарий - значение счетчика будет нулевым (в данном случае - 1). Если же вы откроете еще одно окно броузера (например, в Навигаторе - это команда меню File, New Navigator Window), значение счетчика не обнулится.
Проверить зарегистрирована ли переменная в сессии позволяет функция session_is_register(string $name). Отменить регистрацию какой-нибудь переменной можно с помощью функции session_unregister(string $name). Отменить регистрацию ВСЕХ переменных сессии можно с помощью функции session_unset().

Имя сессии

Теперь рассмотрим еще один пример из жизни. Предположим, вы написали какой-нибудь сценарий с использованием сессий, предположим ту же анкету, и зарегистрировали в нем переменную FirstName. Предположим, пользователь заполняет аналогичную анкету другого разработчика и в ней также используется переменная FirstName - значения переменных перемешаются. Чтобы избежать этого, нужно присвоить нашей сессии какое-нибудь имя, например, SessionOfIvan. Это можно сделать с помощью функции session_name(). Функцию session_name() нужно вызвать ДО инициализации сессии, в противном случае толку от нее будет мало (см. листинг 2). Если имя сессии не указано, PHP будет использовать имя сессии по умолчанию - PHPSESID.


<?
session_name("SessionOfIvan");
session_start();
session_register("a");
?>

Мы уже знаем, для чего используется SID. Что же такое имя сессии? По-простому можно объяснить так: имя сессии - это название группы сессии, к которой принадлежит какой-нибудь SID.
Функция session_id([string $sid]) возвращает значение идентификатора сессии. Эта функция может также и изменить текущий SID, если указать параметр $sid.
Еще одна полезная функция - session_save_path([string $path]). Она возвращает имя каталога, в котором будут находится временные файлы сессии. Обычно это каталог /tmp. С помощью параметра $path можно указать другой каталог.

Использование сессий без Cookies

По статистике, около 20% пользователей по непонятным мне причинам отключают Cookies. Желательно предусмотреть и этот вариант. Перед выполнением следующего примера отключите Cookies в вашем броузере, если это не сделать, то PHP4 будет использовать Cookies и пример будет несколько не так работать.


<?
session_name("SessionofIvanov");
session_start();
session_register("a");
$a=@$a+1;
echo "<html><body>Нажмите Reload, чтобы увеличить счетчик";
echo "<br>Счетчик: $a";
?>
<a href=sesq.php?<?=SID?>>Click here</a>";
Обратите внимание на последнюю ссылку: при отключенных Cookies PHP генерирует  константу SID. Если Cookies включены, данная константа ничего не содержит (см. рис.1).  Для работы нашего сценария первый раз нужно перейти по этой ссылке вместо нажатия на Reload. Потом уже можно будет нажимать и на Reload.
Замечание: наш сценарий называется sesq.php

Рис.1. Константа SID

При использовании константы SID (см. предыдущий листинг) интерпретатору PHP кажется, что данные об идентификаторе и имени сессии пришли из Cookies броузера. Обработчики сессии

PHP4 позволяет переопределить стандартные обработчики сессии, если они почему-то вас не устраивают.
В таблице 1 описаны все стандартные обработчики сессии PHP4.

Таблица 1.
Обработчик Назначение
session_handler_open(string $save_path, string $session_name) Данный обработчик вызывается при инициализации сессии, то есть, когда вызывается функция session_start(). Обычно этот обработчик открывает базу данных (или какой-нибудь файл) для группы сессий $session_name.
session_handler_close()
Данный обработчик вызывается, когда нужно закрыть временное хранилище (вся информация уже записана)
session_handler_read(string $sid)
Функция вызывается, когда нужно прочитать данные сессии. В качестве параметра передается идентификатор сессии - $sid.Функция возвращает данные сессии в формате: name1=value1;name2=value2;...;nameN=valueN; Под значением подразумевается сериализированные с помощью функции Serialize данные. Например, var1|i:10;var2|i:20, в сессии были сохранены две переменные - var1 и var2. Значение первой - 10, второй - 20.
session_handler_write(string $sid, string $data)
Функция предназначена для записи данных сессии с указанным идентификатором. Параметр $data задается в вышеописанном формате.
session_handler_destroy(string $sid) Этот обработчик вызывается перед уничтожением сессии.
session_handler_gc(int $maxlifitime) Обработчик handler_gc вызывается после того, как сценарий завершил свою работу. Если пользователь окончательно "ушел" с вашего сервера, PHP должен удалить данные сессии. Вот именно для это и предназначена функция handler_gc. Параметр $maxlifetime - максимальное время существования сессии в секундах.

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

Для переопределения обработчиков используется функция session_set_save_handler($open,$close,$read,$write,$destroy,$gc).

В качестве параметром нужно передать имена функций, которые будут использоваться как обработчики. Данную функцию нужно вызывать ДО инициализации сессии (вызова функции session_start()), иначе она просто будет проигнорирована.

Теперь рассмотрим пример переопределения обработчиков, который я позаимствовал из официальной документации по PHP (см. листинг 3).

Листинг 3. handlers.php


<?
function open ($save_path, $session_name) 
{
echo "open ($save_path, $session_name)\n";
return true;
}

function close() 
{
echo "close\n";
return true;
}

function read ($key) 
{
echo "write ($key, $val)\n";
return "foo|i:1;";
}

function write ($key, $val) 
{
echo "write ($key, $val)\n";
return true;
}

// Ничего не делает
function destroy ($key) 
{
return true;
}

// Ничего не делает
function gc ($maxlifetime) 
{
return true;
}

session_set_save_handler ("open", "close", "read", "write", "destroy", "gc");

session_start();
$foo++;
?>

Теперь выполните команду php handlers.php
В результате вы должны увидеть примерно следующее:

X-Powered-By: PHP/4.0.0
Set-Cookie: PHPSESSID=7401e923e7aca6d8c66bbf9db9cf6b08
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-cache, post-check=0, pre-check=0
Pragma: no-cache
Content-type: text/html

open (/tmp, PHPSESSID)
write (7401e923e7aca6d8c66bbf9db9cf6b08, )
write (7401e923e7aca6d8c66bbf9db9cf6b08, foo|i:2;)
close

Linux coutner
Hosted by uCoz