главная :: основы PHP
Заголовок
Отслеживание сеанса
Сеансом (session) называется период времени, который начинается с момента прихода пользователя на сайт и завершается, когда пользователь
покидает сайт. В течение сеанса часто возникает необходимость в сохранении различных переменных, которые бы «сопровождали»
пользователя при перемещениях на сайте, чтобы вам не приходилось вручную кодировать многочисленные скрытые поля или переменные,
присоединяемые к URL.
Рассмотрим следующую ситуацию. При входе на сайт пользователю присваивается уникальный идентификатор сеанса (SID), который
сохраняется на компьютере пользователя в cookie с именем PHPSESSJD. Если использование cookie запрещено или cookie вообще не
поддерживаются, SID автоматически присоединяется ко всем локальным URL на протяжении сеанса. В то же время на сервере сохраняется
файл, имя которого совпадает с SID. По мере того как пользователь перемещается по сайту, значения некоторых параметров должны
сохраняться в виде сеансовых переменных. Эти переменные сохраняются в файле пользователя. При последующем обращении к сеансовой
переменной сервер открывает сеансовый файл пользователя и ищет в нем нужную переменную. В сущности, в этом и заключается суть
отслеживания сеанса. Конечно, информация с таким же успехом может храниться в базе данных или в другом файле.
Интересно? Еще бы. После всего сказанного вы, несомненно, лучше поймете различные проблемы конфигурации, рассматриваемые ниже.
Особенно важную роль играют три флага. Первый флаг, --enable-trans-id, включается в процесс конфигурации в том случае, если вы
собираетесь использовать SID (см. ниже). Два других флага, track_vars и register_globals, включаются и отключаются по мере необходимости в
файле php.ini. Последствия активизации этих флагов рассматриваются ниже.
--enable-trans-id
Если РНР компилируется с этим флагом, ко всем относительным URL автоматически присоединяется идентификатор сеанса (SID).
Дополнение записывается в формате имя_сеанса=идентификатор_сеанса, где имя_сеанса определяется в файле php.ini (см. ниже). Если вы не
захотите включать этот флаг, в качестве SID можно использовать константу.
track_vars
Установка флага track_vars позволяет использовать массивы $HTTP_*_VARS[], где * заменяется одним из значений EGPCS (Environment, Get,
Post, Cookie, Server). Данный флаг необходим для того, чтобы значения SID передавались с одной страницы на другую. В РНР 4.03 этот флаг
всегда находится в установленном состоянии.
register_globals
В результате установки этого флага все переменные EGPCS становятся доступными глобально. Если вы не хотите, чтобы массив глобальных
переменных заполнялся данными, которые вам, возможно, и не понадобятся, флаг следует сбросить.
Если флаг register_globals сброшен, а флаг track_vars установлен, ко всем переменным GPC можно обращаться через массив $HTTP_*_VARS[].
Например, если сбросить флаг register_globals, к стандартной переменной $PHP_SELF придется обращаться в виде
$HTTP_SERVER_VARS["PHP_SELF"].
Существует целый ряд других аспектов конфигурации, о которых следует позаботиться. Эти директивы перечислены в табл. 13.1 с указанием
стандартных значений, задаваемых по умолчанию в файле php.ini. Перечисление производится в порядке появления директив в файле.
Таблица 13.1. Сеансовые директивы в файле php.ini
Директива
Описание
session.save_handler = files
Определяет способ хранения сеансовых
данных на сервере. Возможны три варианта:
в файле (files), в общей памяти (mm) или с
использованием функций, определяемых
пользователем (User). Последний вариант
позволяет легко сохранить информацию в
любом формате — например, в базе данных
session.save_path =/tmp
Определяет каталог для сеансовых файлов
РНР. На платформе Linux обычно
используется значение по умолчанию ('/tmp').
На платформе Windows следует указать путь к
какому-нибудь каталогу, в противном случае
произойдет ошибка
session_use_cookies =1
При установке этого флага для сохранения
идентификатора сеанса на компьютере
пользователя используются cookie
session.name =PHPRESSID.
Если флаг session.use_cookies установлен, то
значение session.name используется в
качестве имени cookie. Имя может состоять
только из алфавитно-цифровых символов
session.auto_start = 0
При установке флага session.auto_start сеанс
автоматически инициируется при
первоначальном запросе со стороны клиента
session.cookie_lifetime = 0
Если флаг session.use_cookies установлен, то
значение session.cookie_lifetime определяет
срок действия отправляемых cookie. Если
параметр равен 0, то все cookie становятся
недействительными при завершении сеанса
session.cookie_path = /
Если флаг session.use_cookies установлен, то
значение session.cookie_path определяет
каталог, для которого отправляемые cookie
считаются действительными
session.cookie_domain =
Если флаг session.use_cookies установлен, то
значение session.cookie_domain определяет
домен, для которого отправляемые cookie
считаются действительными
session.serialize_handler = php
Имя обработчика, используемого в процессе
сериализации данных. В настоящее время
определены два возможных значения: php и
WDDX
session.gc_probability =1
Вероятность активизации сборщика мусора
РНР (в процентах)
session.gc_maxlifetime=1440
Промежуток времени (в секундах), по
истечении которого данные сеанса
считаются недействительными и
уничтожаются. Отсчет начинается с момента
последнего обращения пользователя в
текущем сеансе
session.referer_check =
Если этому параметру присвоено строковое
значение, каждый запрос к странице при
включенном отслеживании сеанса
начинается с проверки того, что заданная
строка присутствует в глобальной
переменной $HTTP_REFERER. Если строка
не найдена, идентификаторы сеансов
игнорируются
session.enthropy_fiie =
Ссылка на внешний файл с дополнительной
случайной информацией, используемой при
генерации идентификаторов сеансов. В
системах UNIX для этой цели обычно
используются два устройства, /dev/random и
/dev/urandom. Устройство /dev/random
получает случайные данные от ядра, а
устройство /dev/urandom генерирует
случайную строку при помощи
хэш-алгоритма М05. Короче говоря,
/dev/random работает быстрее, a /dev/urandom
генерирует «более случайные» строки
session.enthropy_length = 0
Если флаг session.enthropy_file установлен, то
session.enthropyjength определяет количество
байт, читаемых из файла session.enthropy_file
session.cache limiter = nocache
Способ управления кэшем для страниц
сеанса. В настоящее время определены три
возможных значения: nocache, public и private
session.cache_expire =180
Продолжительность жизни кэшированных
страниц сеанса (в минутах)
После внесения всех необходимых изменений в настройку сервера мы переходим к непосредственной реализации отслеживания сеанса на
вашем сайте. Благодаря нескольким стандартным функциям РНР этот процесс не так уж сложен. Первое, что необходимо знать, — сеанс
инициируется функцией session_start( ). Конечно, при включении директивы session.auto_start в файл php.ini (см. выше) необходимость в вызове
этой функции отпадает. Тем не менее, в оставшейся части этого раздела я буду использовать эту функцию, чтобы примеры выглядели более
последовательно. Функция session_start( ) имеет простой синтаксис, поскольку она не получает параметров и возвращает логическую величину.
Директива session.save_handler настолько важна, что я счел необходимым посвятить ей отдельный раздел. Он находится в конце
главы под заголовком «Назначение пользовательских функций для хранения сеансовых данных».
session_start( )
Функция session_start( ) имеет двойное назначение. Сначала она проверяет, начал ли пользователь новый сеанс, и если нет — начинает его.
Синтаксис функции
session_start( ): boolean session_start()
Если функция начинает новый сеанс, она выполняет три операции: назначение пользователю SID, отправку cookie (если в файле php.ini
установлен флаг session_cookies) и создание файла сеанса на сервере. Второе назначение функции заключается в том, что она информирует
ядро РНР о возможности использования в сценарии, в котором она была вызвана, сеансовых переменных.
Сеанс начинается простым вызовом session_start( ) следующего вида:
session_start( ):
Если сеанс можно создать, значит, его можно и уничтожить. Это делается функцией session_destroy( ).
Функция session_start( ) возвращает TRUE независимо от результата. Следовательно, проверять ее в условиях if или в команде die( )
бессмысленно.
session_destroy()
Функция session_destroy( ) уничтожает все хранимые данные, относящиеся к сеансу текущего пользователя. Синтаксис функции session_destroy( ):
boolean session_destroy( )
Следует помнить, что эта функция не уничтожает cookie на браузере пользователя. Впрочем, если вы не собираетесь использовать cookie
после конца сеанса, просто присвойте параметру session.cookie_lifetime в файле php.ini значение ( ) (используемое по умолчанию). Пример
использования функции:
session_start( );
// Выполнить некоторые действия для текущего сеанса
session_destroy( ):
?>
Теперь вы умеете уничтожать сеансы, и мы можем перейти к работе с сеансовыми переменными. Возможно, самой важной сеансовой
переменной является SID (идентификатор сеанса). Его легко можно получить при помощи функции session_id( ).
session_id( )
Функция session_id( ) возвращает SID для сеанса, созданного функцией session_start( ). Синтаксис функции session_id( ):
string session_id ([string sfd])
Если в необязательном параметре передается идентификатор, то значение SID текущего сеанса изменяется. Однако следует учитывать, что
cookie при этом заново не пересылаются. Пример:
session_start()
print "Your session identification number is ".sessionjd( ):
session_destroy( ):
?>
Результат, выводимый в браузере, выглядит примерно так:
Your session identification number is 067d992a949114ee9832flcllcafc640
Как же создать свою сеансовую переменную? С помощью функции session_register( ).
session_register( )
Функция session_register( ) регистрирует имена одной или нескольких переменных для текущего сеанса. Синтаксис функции session_register( ):
boolean session_register (mixed имя_переменной1 [, mixed имя_переменной2... ])
Следует помнить, что вы регистрируете не сами переменные, а их имена. Если сеанс не существует, функция session_register( ) также неявно
вызывает session_start( ) для создания нового сеанса.
Прежде чем приводить примеры использования session_register( ), я хочу представить еще одну функцию, связанную с отслеживанием сеанса,
— session_is_registered( ). Эта функция проверяет, была ли зарегистрирована переменная с заданным именем.
session_is_registered( )
Часто требуется определить, была ли ранее зарегистрирована переменная с заданным именем. Задача решается при помощи функции
session_is_registered( ), имеющей следующий синтаксис:
boolean session_is_registered (string имя_переменной)
Применение функций session_register( ) и session_is_registered( ) будет продемонстрировано на классическом примере использования сеансовых
переменных — счетчике посещений (листинг 13.5).
Листинг 13.5. Счетчик посещений сайта пользователем
session_start( ):
if (! sessionjs_registered('hits')) :
session_register( 'hits' ) ;
endif ;
$hits++:
print "You've seen this page $hits times.
?>
Сеансовые переменные можно не только создавать, но и уничтожать. Для этой цели применяется функция session_unregister( ).
session_unregister( )
Сеансовые переменные уничтожаются функцией session_unregister( ). Синтаксис:
boolean session_unregister (string имя_переменной')
При вызове функции передается имя сеансовой переменной, которую вы хотите уничтожить.
session_start()
session_register('username');
// Использовать переменную $username.
// Когда переменная становится ненужной - уничтожить ее.
session_unregister('username');
session_destroy();
?>
Как и в случае с функцией session_register, помните, что в параметре указывается не сама переменная (то есть имя с префиксом $). Вместо этого
указывается имя переменной.
session_encode( )
Функция session_encode( ) обеспечивает чрезвычайно удобную возможность форматирования сеансовых переменных для хранения (например,
в базе данных). Синтаксис функции session_encode( ):
boolean session_encode( )
В результате выполнения этой функции все сеансовые данные форматируются в одну длинную строку, которую можно сохранить в базе
данных.
Пример использования session_encode( ) приведен в листинге 13.6. Предположим, что на компьютере «зарегистрированного» пользователя
имеется cookie, в котором хранится уникальный идентификатор этого пользователя. Когда пользователь запрашивает страницу, содержащую
листинг 13.6, UID читается из cookie и присваивается идентификатору сеанса. Мы создаем несколько сеансовых переменных и присваиваем
им значения, после чего форматируем всю информацию функцией session_encode( ) и заносим в базу данных MySQL.
Листинг 13.6. Использование функции session_encode( ) для сохранения данных в базе данных MySQL
// Инициировать сеанс и создать сеансовые переменные
session_register('bgcolor');
session_register('fontcolor');
// Предполагается, что переменная $usr_id (с уникальным
// идентификатором пользователя) хранится в cookie
// на компьютере пользователя.
// При помощи функции session_id( ) присвоить идентификатору
// сеанса уникальный идентификатор пользователя (UID),
// хранящийся в cookie. $id = session_id($usr_id);
// Значения следующих переменных могут задаваться пользователем
// на форме HTML $bgcolor = "white"; $fontcolor = "blue";
// Преобразовать все сеансовые данные в одну строку
$usr_data = session_encode( );
// Подключиться к серверу MySQL и выбрать базу данных users
@mysql_pconnect("localhost", "web", "4tf9zzzf")
or die("Could not connect to MySQL server!");
@mysql_select_db("users")
or die("Could not select user database!");
// Обновить пользовательские параметры страницы
$query = "UPDATE user_info set page_data='$usr_data' WHERE user_id= '$id'";
$result - mysql_query($query) or die("Could not update user information!");
?>
Как видите, быстрое преобразование всех сеансовых переменных в одну строку избавляет нас от необходимости создавать несколько полей
для хранения/загрузки данных, а также несколько уменьшает объем программы.
session_decode( )
Все сеансовые данные, ранее преобразованные в строку функцией sessi on_encode( ), восстанавливаются функцией session_decode( ). Синтаксис:
string session_decode (string сеансовые_данные)
В параметре сеансовые_данные передается преобразованная строка сеансовых переменных, возможно — прочитанная из файла или
загруженная из базы данных. Строка восстанавливается, и все сеансовые переменные в строке преобразуются к исходному формату.
В листинге 13.7 продемонстрировано восстановление закодированных сеансовых переменных функцией session_decode( ). Предположим,
таблица MySQL с именем user_info состоит из двух полей: user_id и page_data. Пользовательский UID, хранящийся в cookie на компьютере
пользователя, применяется для загрузки сеансовых данных, хранящихся в поле page_data. В этом поле хранится закодированная строка
переменных, одна из которых ($bgcolor) содержит цвет фона, выбранный пользователем.
Листинг 13.7. Восстановление сеансовых данных, хранящихся в базе данных MySQL
// Предполагается, что переменная $usr_id (с уникальным
// идентификатором пользователя) хранится в cookie
// на компьютере пользователя.
$id = session_id($usr_id);
// Подключиться к серверу MySQL и выбрать базу данных users
@mysq]_pconnect("localhost", "web", "4tf9zzzf")
or die("Could not connect to MySQL server!");
@mysql_select_db("users")
or die("Could not select company database!");
// Выбрать данные из таблицы MySQL
$query = "SELECT page_data FROM user_info WHERE user_id= '$id'",
Sresult = mysql_query($query);
$user_data = mysql_result($result, 0. "page_data");
// Восстановить данные session_decode($user_data):
// Вывести одну из восстановленных сеансовых переменных
print "BGCOLOR: $bgcolor";
?>
Как видно из двух приведенных листингов, функции session_ encode( ) и ses-sion_decode( ) обеспечивают очень удобные и эффективные
сохранение и загрузку сеансовых данных.
Назначение пользовательских функций для хранения сеансовых данных
Хранить сеансовые данные в файлах удобно, но вполне возможно, вы захотите воспользоваться другими средствами — например, базами
данных. А может быть, вы хотите применить один и тот же сценарий на разных сайтах для разных баз данных. Существует и другая
распространенная проблема — стандартная для РНР процедура хранения сеансовых данных в файлах затрудняет совместное использование
данных на разных серверах. К счастью, все эти проблемы отслеживания сеансов в РНР решаются очень просто, поскольку РНР дает
пользователю возможность установить собственную процедуру сохранения при помощи стандартной функции session_set_save_handler( ).
Функция session_set_save_handler( ) определяет процедуры сохранения и загрузки сеансовых данных пользовательского уровня.
Синтаксис функции session_set_save_handler():
void session_set_save_handler (string open, string close, string read, string write, string
destroy, string go)
Шесть параметров session_set_save_handler( ) соответствуют шести функциям, вызываемым сеансовыми функциями РНР. Хотя имена этих
функций могут быть произвольными, каждая функция должна получать жестко заданный набор параметров. Перед тем как переходить к
рассмотрению примера, просмотрите таблицу 13.2 — в ней описаны назначение всех шести функций и их параметры.
Чтобы использовать функцию session_set_save_handler( ), необходимо присвоить па-раметру session.save_handler в файле php.ini
значение user.
Таблица 13.2. Шесть параметров функции session_set_save_handler( )
Параметр
Описание
sess_close( )
Вызывается при завершении сценария, в
котором реализуются сеансовые функции. Не
путайте эту функцию с функцией sess_destroy( ),
предназначенной для уничтожения сеансовых
переменных. Функция sess_close( ) вызывается
без параметров
sess_destroy($идент_ceaнca)
Удаляет все сеансовые данные. Параметр
определяет удаляемый сеанс
sess_gc($срок_действия)
Удаляет все сеансы с завершенным сроком
действия. Срок определяется параметром
$срок_действия, значение которого задается в
секундах. Параметр читается из файла php.ini и
соответствует значению session.gcjifetime
sess_open($путь, $имя)
Вызывается при инициализации нового сеанса
функцией session_start( ) или session_register( ).
Два параметра читаются из файла php.ini и
соответствуют значениям session.save_path и
session.name
sess_read($ключ)
Используется для выборки значения сеансовой
переменной, определяемой заданным ключом
sess_write($ключ, $значение)
Используется для сохранения сеансовых данных.
Любые данные, сохраненные функцией
sess_write( ), позднее могут быть прочитаны
функцией sess_read( ). Параметр $ключ
соответствует имени сеансовой переменной, а
параметр $значение — значению, связываемому
с заданным ключом
Теперь, когда вы знаете все, что необходимо знать о параметрах session_set_save_handler( ), мы рассмотрим пример реализации сеансовых
функций на базе MySQL (листинг 13.8).
Листинг 13.8. Реализация сеансовых функций на базе MySQL
// Реализация сеансовых функций на базе MySQL
// Хост, имя пользвателя и пароль
$host = "localhost"; $user = "web"; $pswd = "4tf9zzzf";
// Имена таблицы и базы данных
$db = "users";
$session table = "user session data";
// Прочитать значение sess.gc_lifetime из файла php.ini
$sess_life = get_cfg_var("sess.gc_lifetime");
// Функция : mysql_sess_open()
// Назначение: подключение к серверу MySQL
// и выбор базы данных.
function mysql_sess_open($save_path. $session_name) {
GLOBAL $host. $user, $pswd, $db;
@mysql_connect($host, $user, $pswd)
or die("Can't connect to MySQL server!");
@mysql_select_db($db)
or die("Can't select session database!");
}
// Функция: mysql_sess_close()
// Назначение: в реализации на базе MySQL эта функция не используется.
// Тем не менее, она Обязательно* должна быть определена.
function diysql_sess_close() {
return true:
}
// Функция: mysql_sess_read()
// Назначение: загрузка информации из базы данных MySQL.
function mysql_sess_read($key) {
GLOBAL $session_table:
$query = "SELECT value FROM $session_table WHERE sess_key = '$key'";
$result = mysql_query( $query);
if (list($value) = mysql_fetch_row($result)) :
return $value;
endlf;
return false;
}
// Функция: mysql_sess_write( )
// Назначение: запись информации в базу данных MySQL.
function mysql_sess_write($key, $val) {
GLOBAL $sess_life, $session_table;
$expiratlon = time() + $sess_life;
$query = "INSERT INTO Ssession_table VALUES('$key', '$expiration', '$value')";
$result = mysql_query($query);
// Если запрос на вставку данных завершился неудачей // из-за присутствия первичного ключа в поле
sess_key, // выполнить обновление.
if (! $result) :
$query = "UPDATE $session_table
SET sess_expiration = '$expiration', sess_value='Svalue'
WHERE sess_key = '$key'"; $result = mysql_query($result);
endif;
}
// Функция: mysql_sess_destroy()
// Назначение: удаление из таблицы всех записей с ключом, равным $sess_id
function mysql_sess_destroy(Ssess_id) {
GLOBAL $session_table:
$query = "DELETE FROM $session_table WHERE sess_key = '$sess_id'";
$result = mysql_result($query);
return $result;
}
// Функция: mysql_sess_gc()
// Назначение: удаление всех записей, у которых
// срок жизни < текущее время - session.gc_lifetime
function mysql_sess_gc($max_lifetime) {
GLOBAL $session_table:
$query = "DELETE FROM $session_table WHERE sess_expiration < ".time();
$result = mysql_query($query);
return mysql_affected_rows();
session_set_save_handler("mysql_sess_open", "mysql_sess_close","mysql_sess_read",
"mysql_sess_write", "mysql_sess_destroy", "mysql_sess_gc");
?>
После того как эти шесть функций будут зарегистрированы в программе, их можно вызывать по абстрактным именам (sess_close( ), sess_destroy(
), sess_gc( ), sess_open( ), sess_read( ) или sess_write( )). Такой подход удобен тем, что вы можете создать сколько угодно реализаций и
переключаться между ними, вызывая ses-sion_set_save_handler( ) по мере необходимости.
|