Войти через VK Войти через FB Войти через Google Войти через Яндекс
Поиск по сайту
Использование MySQL для хранения данных сессий
Для высоко нагруженных проектов использование файлов для хранения файлов сессий становится недопустимым.
В этой статье мы рассмотрим использвоание БД MySql для хранения данных сессий.
- все настройки сессий производятся до старта сессии, поэтому необходимо отменить автостарт сессий:
ini_set('session.auto_start', '0');
- стандартно PHP хранит сессии в файлах, чтобы установить свои обработчики сессий определим:
ini_set('session.save_handler', 'user');
у 'session.save_handler' может быть три значения:- files - значение по умолчанию, PHP использует стандартные функции обработки сессий, сессии храняться в файлах, необходимо определить ini_set('session.save_path', путь); место для хранения файлов сессий.;
- mm - PHP использует стандартные функции обработки сессий, сессии храняться в памяти;
- user - позволяет переопределять стандартные функции обработки сессий, и соответственно в этих функциях указывать, где мы будем хранить сессии и как мы будем их обрабатывать.
- теперь определим функции обработки сессий:
рассмотрим каждую:session_set_save_handler ( "sess_open", "sess_close", "sess_read", "sess_write", "sess_destroy", "sess_gc");
- sess_open - открывает сессию. Функция создает уникальное ID сессии. Требует для своей работы два параметра 'session.save_path' и 'session.name'.
Т.к. мы храним сессии в базе, то 'session.save_path' нам не нужен, а вот 'session.name' можно определить вместо стандартного - 'PHPSESSID'. Итак дописываем в конфигурацию:
ini_set ('session.name', 'SID');
- sess_close - закрывает сессию (не разрушая сессионные переменные).
- sess_read - читает данные из временного хранилища, в нашем случае из базы. Требует ID сессии, что из таблицы сессии надо прочитать и записать в сессию из таблицы сессий.
- sess_write - пишет данные во временное хранилище. Требует ID сессии, и пишет все из сессии в базу.
- sess_destroy - разрушает сессию. Требует ID сессии. Для удаления информации существует следующая функция.
- sess_gc - это просто сборщик мусора. Требует срок хранения сессий во временном хранилище в секундах определенного в параметре 'session.gc_maxlifetime'
(по умолчанию 30 минут). Определяем его, и определим время жизни сессионной куки:
ini_set('session.gc_maxlifetime', XXX); ini_set('session.cookie_lifetime', YYY);
'sess_gc' не всегда вызывается при инициализации сессии, есть еще одна настройка которая управляет этим параметром - 'session.gc_probability'. Этот параметр определяет вероятность запуска 'sess_gc' в процентах, соответственно валидные значения 1-100. Значение по умолчанию 1%. Т.е. это означает, что с вероятностью в 1%, при открытии новой странице сайта, будет происходить очистка сессионной таблицы, по моему опыту оптимально значение 5-10. Добавляем к конфигурации:
ini_set ('session.gc_probability', 5);
- sess_open - открывает сессию. Функция создает уникальное ID сессии. Требует для своей работы два параметра 'session.save_path' и 'session.name'.
Т.к. мы храним сессии в базе, то 'session.save_path' нам не нужен, а вот 'session.name' можно определить вместо стандартного - 'PHPSESSID'. Итак дописываем в конфигурацию:
Структура таблиц:
CREATE TABLE "session" (
session_id character varying(32) NOT NULL,
session_user_id integer DEFAULT 0 NOT NULL,
session_counter integer DEFAULT 0 NOT NULL,
session_ip character varying(16),
session_agent character varying(255),
session_last integer DEFAULT 0 NOT NULL,
session_created integer DEFAULT 0 NOT NULL,
session_data text
);
CREATE TABLE "user" (
user_id character varying(32) NOT NULL,
user_ip character varying(16),
user_agent character varying(255),
/* могут быть и другие поля */
);
session.php - Хранение данных сессии в MySQL таблице и функции работы с сессиями на PHP.
Используется глобальный массив $user[] с полями из таблиц БД session, user.
Подразумевается что соединение с MySQL уже установлено и определено в глобальной переменной $db.
Текущая информация сохраняется в глобальной переменной $session.
<?
$SERVER_NAME=$_SERVER['HTTP_HOST'];
$SERVER_NAME=preg_replace('/^http:\/\//', '', $SERVER_NAME);
$SERVER_NAME=preg_replace('/^www\./', '', $SERVER_NAME);
define("CookiePath","/");
define("CookieDomain",$SERVER_NAME); //".".$SERVER_NAME домен
define("live_sess_time","1000");
ini_set('session.auto_start', '0'); // автостарт сессий не нужен
ini_set('session.use_cookies', '1');// передавать идентификатор сессии в куках
ini_set('session.use_trans_sid', '0'); // не передавать идентификатор сессии добавляя его к URL и формам
ini_set('session.save_handler', 'user');
ini_set('session.name', 'SID'); // Имя сессии
ini_set('session.gc_maxlifetime', '1800'); // время жизни сессии, 30 минут (60*30)
//ini_set ('session.cookie_lifetime', '2000'); // 0 - кука умирает при закрытии браузера
// Задаем параметры сессионной куки: (время жизни= 0 - умрет при закрытии браузера, путь, домен, true= доступно только из https зоны)
session_set_cookie_params (0, CookiePath, CookieDomain, false);
//Выставляем вероятность запуска функции sess_gc в процентах (допустимые значения 1-100, по умолчанию равно 1%)
ini_set('session.gc_probability', 10);
function sess_open ($save_path, $session_name) {return true;}
function sess_close () {return true;}
function sess_read ($session_id) {
global $db, $user, $session;
if (strlen ($session_id) != 32) {
error_log ("sess_read(): Invalid SessionID = ".$session_id);
return '';
}
$sql = "SELECT `session_id`, `session_user_id`, `session_counter`, `session_ip`, `session_agent`, `session_data`
FROM `session`
WHERE `session_id` = '".$db->sql_escape($session_id)."' AND `session_last` > '".(time() - live_sess_time)."'";
$result = $db->sql_query ($sql);
if ($db->sql_numrows ($result) == 1) {
$session = $db->sql_fetchrow ($result);
if ($session AND $session['session_ip'] == $user['user_ip'] AND $session['session_agent'] == $user['user_agent']) {
// выборка информации о пользователе. TODO замените при необходимости на свою !!!
$sql = "SELECT * FROM `user`
WHERE `user_id` = '".$db->sql_escape($session['session_user_id'])."' LIMIT 1";
$result = $db->sql_query ($sql);
if(!$result) {
$result = $db->sql_error ($result);
error_log ('sess_read(): Failed to read user info - '.$result['message']);
return '';
}
else {
$user_data = $db->sql_fetchrow ($result);
$user = array_merge ($user, $user_data, $session); // слить три массива в один
unset($user['session_data']);
return $session['session_data'];
}
} else {
if (isset($_REQUEST[session_name()])) sess_destroy($_REQUEST[session_name()]);
return '';
}
} elseif (!$result) {
$result = $db->sql_error ($result);
error_log ('sess_read(): Failed to read sessions - '.$result['message']);
return '';
} else {
$session = NULL;
if (isset($_REQUEST[session_name()])) sess_destroy($_REQUEST[session_name()]);
return '';
}
}
function sess_write ($session_id, $session_data) {
global $db, $user, $session;
if (strlen ($session_id) != 32) {
error_log ('sess_write(): Invalid Session ID = '.$session_id);
return false;
}
if (4294967295 < strlen($session_data)) {
error_log ('sess_write(): Session data too large. '.$session_id.'(max. 4294967295) -> '.strlen($session_data));
if (isset($_REQUEST[session_name()])) sess_destroy($_REQUEST[session_name()]);
return false;
}
if ($session AND $session['session_ip'] != $user['user_ip']){
if (isset($_REQUEST[session_name()])) sess_destroy($_REQUEST[session_name()]);
return false;
}
if ($session) {
$sql = "UPDATE `session`
SET `session_user_id` = '".intval ($session['session_user_id'])."',
`session_last` = '".time ()."',
`session_counter` = '".intval(++$session['session_counter'])."',
`session_data` = '".$db->sql_escape($session_data)."'
WHERE `session_id` = '".$db->sql_escape($session_id)."' LIMIT 1";
} else {
$sql = "INSERT INTO `session` (`session_id`, `session_created`, `session_last`,
`session_ip`, `session_agent`, `session_data`)
VALUES ('".$db->sql_escape ($session_id)."', ".time().", ".time().",
'".$db->sql_escape ($user['user_ip'])."',
'".$db->sql_escape ($user['user_agent'])."',
'".$db->sql_escape ($session_data)."')";
}
$result = $db->sql_query ($sql);
if (!$result) {
$result = $db->sql_error ($result);
error_log ('sess_write(): Failed to INSERT/UPDATE session. '.$result['message']."<br> Query: ".$sql);
return false;
}
return true;
}
function sess_destroy ($session_id) {
global $db;
$sql = "DELETE FROM `session`
WHERE `session_id` = '".$db->sql_escape ($session_id)."'";
$result = $db->sql_query ($sql);
if (!$result) {
$result = $db->sql_error ($result);
error_log ('sess_destory(): Failed to DELETE session. '.$result['message']);
return false;
}
return true;
}
function sess_gc ($sess_gc_maxlifetime) {
global $db;
$sql = "DELETE FROM `session` WHERE `session_last` < '".(time () - $sess_gc_maxlifetime)."'";
$result = $db->sql_query ($sql);
if (!$result) {
$result = $db->sql_error ($result);
error_log ('sess_gc(): Failed to DELETE old sessions.'.$result['message']);
return false;
}
$sql = "OPTIMIZE TABLE `session` ";
$result = $db->sql_query ($sql);
if (!$result) {
$result = $db->sql_error ($result);
error_log ('sess_gc(): Failed to OPTIMIZE sessionstable.'.$result['message']);
return false;
}
return true;
}
session_set_save_handler ("sess_open", "sess_close", "sess_read", "sess_write", "sess_destroy", "sess_gc");
// Можно активировать при проблемах
register_shutdown_function ('session_write_close');
session_start ();
?>
При этом в базе остаются записи только о тех сессиях, которые сейчас активны, просроченные сессии удаляются, изменяя значение 'session.gc_probability' вы можете подобрать тот балланс который подходит Вашему сайту. Таким образом регулировать размер таблицы сессий.
Ещё примеры работы с сесиями на PHP
Читать дальше: Безопасность в PHP
.
Прокомментировать/Отблагодарить