Мой первый хабратопик, надеюсь, что не последний.
Представим ситуацию: есть корзина покупок на сайте, при добавлении в корзину мы ставим на товар т.н. lock, исключающий его из списка доступных для покупки товаров. Когда клиент удаляет товар из корзины — lock снимается. Но что делать, если пользователь просто закрыл браузер? В таком случае сессия будет удалена сборщиком мусора, а локи так и останутся.
Когда я столкнулся с такой ситуацией, первое что мне пришло в голову — хранить локи и дату доступа в БД и периодически дергать её кроном. Но костыльность этого решения очевидна. А вот ещё бред, с которым я столкнулся при решении сабжа: для сериализации и десереализации сессий используются функции и формат, отличные от функций serialize и unserialize. Приходится делать велосипеды для ансериализации сессии.
Ближе к телу: как решил проблему я…
Тут надо сделать замечание, что идею для такого решения мне подал хабраюзер rigid, а помогли решить пару около-сабжевых проблем в конференции php@conference.jabber.ru.
PHP позволяет определять свои функции для обработки сессий. Отвечает за это функция session_set_save_handler. В качестве параметров она принимает список функций, который будут вызываться для работы с сессиями. В мануале есть даже пример, который реализует стандартный механизм работы с сессиями. Его-то мы и возьмем, изменив только функцию gc, которая занимается сборкой мусора, т.е. удалением файлов мертвых сессий.
Пример функции gc:
Код функции unserializesession, взятый откуда-то из интернета (скорее всего из комментариев к функции в мануале PHP):
Теперь подключаем это в наш проект:
Есть одно но: в Debian/Ubuntu свой механизм очистки сессий, который выполняется кроном, а у PHP нет возможности удалять файлы сессий. Мне это не понравилось, т.к. ломает функционал PHP подменяя его своим сборщиком мусора для сессий. Решить проблему можно задав собственный каталог для файлов сессии и закрыв его в .htaccess (если он находится в document_root).
P.S. Честно говоря я не уверен, что мне можно было публиковать пост сразу в блог PHP. Я не активный пользователь хабра и не знаю местных порядков. Прошу не ругаться.
Представим ситуацию: есть корзина покупок на сайте, при добавлении в корзину мы ставим на товар т.н. lock, исключающий его из списка доступных для покупки товаров. Когда клиент удаляет товар из корзины — lock снимается. Но что делать, если пользователь просто закрыл браузер? В таком случае сессия будет удалена сборщиком мусора, а локи так и останутся.
Когда я столкнулся с такой ситуацией, первое что мне пришло в голову — хранить локи и дату доступа в БД и периодически дергать её кроном. Но костыльность этого решения очевидна. А вот ещё бред, с которым я столкнулся при решении сабжа: для сериализации и десереализации сессий используются функции и формат, отличные от функций serialize и unserialize. Приходится делать велосипеды для ансериализации сессии.
Ближе к телу: как решил проблему я…
Тут надо сделать замечание, что идею для такого решения мне подал хабраюзер rigid, а помогли решить пару около-сабжевых проблем в конференции php@conference.jabber.ru.
PHP позволяет определять свои функции для обработки сессий. Отвечает за это функция session_set_save_handler. В качестве параметров она принимает список функций, который будут вызываться для работы с сессиями. В мануале есть даже пример, который реализует стандартный механизм работы с сессиями. Его-то мы и возьмем, изменив только функцию gc, которая занимается сборкой мусора, т.е. удалением файлов мертвых сессий.
Пример функции gc:
/* Функция принимает в качестве параметра время жизни сессии */
function gc($maxlifetime)
{
global $sess_save_path; /* путь, где лежат сессии */
foreach (glob("$sess_save_path/sess_*") as $filename)
{
/* Проверяем не пора ли убить сессию */
if (filemtime($filename) + $maxlifetime < time())
{
$tmp_sess=unserializesession(file_get_contents($filename)); /* $tmp_sess у нас теперь аналогична $_SESSION той сессии */
/* Обрабатываем данные, например снимаем локи из этой сессии */
@unlink($filename); /* Удаляем сессию */
}
}
return true;
}
* This source code was highlighted with Source Code Highlighter.
Код функции unserializesession, взятый откуда-то из интернета (скорее всего из комментариев к функции в мануале PHP):
function unserializesession($data) {
$vars=preg_split('/([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff^|]*)\|/',
$data,-1,PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE);
for($i=0; $vars[$i]; $i++) $result[$vars[$i++]]=unserialize($vars[$i]);
return $result;
}
* This source code was highlighted with Source Code Highlighter.
Теперь подключаем это в наш проект:
session_set_save_handler("open", "close", "read", "write", "destroy", "gc");
/* Вероятность чистки мусора на каждый session_start() примерно равна 30%, другими словами - чистка мертвых сессий будет производится при тридцати вызовах session_start() из ста */
ini_set("session.gc_probability", 30); /* Можно настроить на 100%, если у вас там нет никакого медленного кода */
ini_set("session.gc_divisor", 100);
ini_set("session.gc_maxlifetime", 1800); /* Время жизни сессии в секундах (то самое, которое передается в функцию gc) */
session_start();
* This source code was highlighted with Source Code Highlighter.
Есть одно но: в Debian/Ubuntu свой механизм очистки сессий, который выполняется кроном, а у PHP нет возможности удалять файлы сессий. Мне это не понравилось, т.к. ломает функционал PHP подменяя его своим сборщиком мусора для сессий. Решить проблему можно задав собственный каталог для файлов сессии и закрыв его в .htaccess (если он находится в document_root).
P.S. Честно говоря я не уверен, что мне можно было публиковать пост сразу в блог PHP. Я не активный пользователь хабра и не знаю местных порядков. Прошу не ругаться.