|
|
|
Статья
Опять возвращаюсь к хранению данных сессии. В этот раз я расскажу, как сделать свой обработчик, как его активировать, и какие подводные камни я нашел. Работа с данными сессии разделена на 6 разных этапов. Соответственно, и обработчик сессии должен перехватить все 6 функций. Вот их условные названия: open, read, write, delete, gc (garbage collection) и close. Я специально не поставил круглые скобки после имен функций, чтоб они в тексте не путались со встроенными функциями PHP. Рассмотрим их работу подробнее: - open
Назначение этой функции - подготовка ко всем остальным операциям. Функция вызывается автоматически сразу после старта сессии. В параметрах ей передаются путь к директории, где предполагается хранить файлы с данными сессий, и имя сессии. Путь берется из php.ini ("session.save_path") либо устанавливается в программе функцией session_save_path(). Имя сессии может использоваться для составления имени файла с данными, а можно его просто игнорировать - вероятность повтора строки MD5 крайне мала. В литературе встречается мнение, что если данные сессии хранятся в базе данных, то в этой функции можно произвести подключение к БД. Я считаю, если БД есть и используется, то не только для хранения данных сессии, и подключение можно произвести раньше, в основной программе. Так проще и понятней. - read
Назначение - считать данные сессии. Функция вызывается сразу после open. Единственный параметр - идентификатор сессии. Вернуть функция должна строку, соответствующую формату входного параметра функции unserialize(). Если данных нет, то она должна вернуть пустую строку. В этой функции также важно понять, есть ли источник данных, т.к. сессия могла быть ранее уничтожена сборщиком мусора (gc) или функцией delete. Также здесь можно проверять и устанавливать блокировки (об этом расскажу в последующих частях). - write
Назначение - запись данных сессии. Функция автоматически вызывается при следующих обстоятельствах: завершение сессии по желанию программиста и по завершению программы (когда вывод в браузер окончен и сообщения об ошибках отобразить в браузере уже нельзя). Входных параметров два: идентификатор сессии и сохраняемые данные в сериализованном виде. Возвращаемое значение должно символизировать результат работы, но и отрицательный результат уже ни на что не повлияет, если вывод завершен (данные не сохранены, работа окончена, моем руки). Не стоит надеяться, что раз источник данных был доступен функции read, то в write не надо проверять его доступность. Файл (или запись в БД) вполне могли уже удалить. По крайней мере, есть такая вероятность, и проверки будут не лишними. Правда, если в read отрыть файл, сохранить его дескриптор и не закрывать, то в write файл будет гарантированно существовать (в *nix системах он может быть еще удален, но физически писать в него еще можно), если конечно не додуматься до хранения временных файлов на сетевом диске или съемном носителе. (? delete) Принудительная запись осуществляется функцией session_write_close(). Начиная с PHP 4.3.11, у нее есть более благозвучное имя - session_commit(). - delete
Назначение - удалить данные сессии. Вызывается автоматически некоторыми встроенными функциями. Например, session_destroy() (с версии 4.3.3), или session_regenerate_id() (с версии 5.1.0). Входной параметр один - идентификатор сессии. Так стоит проверить, существует ли объект для удаления, или, по крайней мере, подавить вывод ошибок - в их обработке смысла нет. - gc
Назначение - "сборка мусора" (удаление ненужных данных). Разработчики PHP сделали предположение, что этот процесс может быть медленным, и поэтому эта функция вызывается не каждый раз. Периодичность запуска определяется настройками php.ini ("session.gc_divisor" и "session.gc_probability"): session.gc_probability/session.gc_divisor - это "вероятность" (chance) запуска сборщика мусора. Входной параметр - максимальное время жизни для сессии в секундах. Значение его берется из php.ini ("session.gc_maxlifetime"), либо может быть установлено в программе через ini_set(). Также можно игнорировать этот параметр и использовать свое значение. Если данные сессий хранятся в индивидуальных файлах, то рекомендуется в проверке использовать время изменения файла, т.к. в ОС Windows может быть неправильная обработка времени доступа. Например, как утверждается на сайте www.php.net, Windows98 не сохраняет время доступа, а только дату. Запускать web-сервер под Windows98 - это, на сегодняшний день, выглядит странно, но береженого Бог бережет - лучше использовать время модификации. С другой стороны, встроенный по умолчанию обработчик и так работает с файлами, и создавать свой аналогичный обработчик нет необходимости, если, конечно, он не выполняет каких-то особых действий, которые в штатном обработчике не предусмотрены. - close
Назначение - завершающие операции. Это уникальная функция, по сравнению с остальными пятью. Параметров - нет. Условия работы - особые: вывод окончен, все объекты разобраны. Т.е., на эту роль подходит либо глобальная функция, либо статический метод класса, а метод объекта недопустим. И самое главное: она, в общем-то, не нужна. Не отрицаю, что возможно есть и ей применение, но два наиболее распространенных хранилища - файлы и СУБД MySQL - прекрасно обходятся без этой функции. Файлы, если их по какой-либо причине не закрыли, закроются автоматически по завершении программы. То же произойдет с подключением к серверу базы данных.
В завершение этой части я привожу пример обработчика, который, подобно штатному, хранит данные в индивидуальных файлах. Для разнообразия и примера я его сделал в виде класса. <?php class session_std { var $_path = ''; var $_fd = false;
function session_open($path, $name) { $this->_path = $path; // Сохраним путь - его нам больше не скажут return true; }
function session_close() { return true; // А делать-то и нечего }
function session_read($sid) { $file = "$this->_path/$sid"; if (!file_exists($file)) // Файл был кем-то удален? return ""; if (!($this->_fd = @fopen($file, 'rb+'))) return ""; // Не удалось открыть $data = fread($this->_fd, filesize($file)); return $data; // Ничего с данными делать не надо - просто возвращаем }
function session_write($sid, $data) { if ($this->_fd === false) // Если в session_read() не удалось открыть { if ($this->_fd = @fopen($file, 'wb')) // попытка создать файл return false; } rewind($fd); // Мотаем на начало ftruncate($fd, 0); // очистить файл $res = fwrite($this->_fd, $data); fclose($this->_fd); return $res; }
function session_delete($sid) { $file = "$this->_path/$sid"; if (!is_file($file)) return false; return unlink($file); // Просто удаляем }
function session_gc($lifetime) { if (!($dir = opendir($this->_path))) return false; while($file = readdir($dir)) { $filepath = "$this->_path/$file"; if (!is_file($filepath)) // А вдруг это директория? continue; $time = filemtime($filepath); // Используем время изменения $now = time(); if ($time + $lifetime < $now) unlink($filepath); } closedir($dir); return true; // Стало в доме меньше пыли } }
$storage = new session_std;
session_set_save_handler( array(&$storage, 'session_open'), array('session_std', 'session_close'), // См. описание close array(&$storage, 'session_read'), array(&$storage, 'session_write'), array(&$storage, 'session_delete'), array(&$storage, 'session_gc') );
session_save_path("/tmp/my_session_dir/"); // Теперь мусорим сюда session_name("MY_SESSION_ID"); // Имя у сессии будет такое
session_start(); ?> Пока все. Продолжение - в следующей статье. Роман Чернышов (RXL) 26.03.2006
|
|
|