Хранение сессий в MySQL. Как в PHP выбрать другое хранилище данных?

хранение сессий в mysql. как в php выбрать другое хранилище данных?

Минусы использования файлов

По умолчанию для хранения сессий в PHP используется файловая система. В большинстве случаев это не создает никаких проблем. Однако специфичные приложения могут столкнуться с рядом ограничений. Во первых взаимодействие с файлами считается довольно медленным. Операции ввода-вывода всегда являются узким местом в общей производительности. Во вторых хранение сессий в файлах не позволяет вашему приложению масштабироваться должным образом.

Программы, обрабатывающие большое количество запросов, работают на нескольких физических серверах. Балансировка нагрузки между ними осуществляется за счет проксирующего веб-сервера. Может случиться так, что файл сессии будет создан на одном сервере, а последующий запрос пользователя распределится на другой сервер, где этого файла нет.

Выбор другого хранилища сессий

Чтобы решить изложенные выше проблемы, PHP позволяет самостоятельно выбрать другой механизм хранения сессий. Для этого необходимо использовать специальную функцию session_set_save_handler(). Она имеет две сигнатуры принимаемых параметров.

В первом случае функция принимает несколько значений типа callable. Эти callback-функции отвечают за конкретные операции над сессиями пользователей. В общем виде сигнатуру вызова можно записать следующим выражением «session_set_save_handler(callable $open, callable $close, callable $read, callable $write, callable $destroy, callable $gc)».

Роль callback-функций и их параметры:

  • open(string $savePath, string $sessionName) — выполняется при открытии сессии с помощью функции session_start(). Предназначена для логики инициализации. Принимает место хранения сессии, заданное в настройках, и её имя. Должна возвращать true или false.
  • close() - выполняется при закрытии сессии, как правило, после записи данных в хранилище, т.е вызова callback с именем write(). Такая процедура может произойти автоматически после завершения скрипта или использования функции session_write_close(). Также должна возвращать логическое значение, свидетельствующее о корректности завершения.
  • read(string $sessionId) — обработчик вызывается единожды при инициализации сессии, сразу после выполнения open(). Этот callback должен возвращать пустую или сериализованную строку с данными. PHP автоматически десериализует её содержимое и заполнит им суперглобальный массив $_SESSION.
  • write(string $sessionId, string $data) — запускается на выполнение перед close(). В качестве параметров принимает строку идентификатор сессии и сериализованный набор данных для записи в хранилище.
  • destroy(string $sessionId) — обработчик уничтожения сессии. Данный callback будет вызван при использовании функции session_destroy() или session_regenerate_id(). Должна вернуть true в случае успеха или false, если произошли ошибки.
  • gc($lifetime) — callback обработки сборки мусора. Вызывается на выполнение периодически в зависимости от значений опций session.gc_probability и session.gc_divisor конфигурационного файла php.ini. Параметр $lifetime берется из опции session.gc_maxlifetime. Он определяет максимальное время, после которого данные считаются «мусором» и подлежат удалению. Назначение обработчика gc() - найти и удалить все «мусорные» сессии. Функция должна вернуть булево значение.

Вторая сигнатура функции session_set_save_handler() принимает всего один обязательный параметр. Им должен быть объект имплементирующий интерфейс SessionHandlerInterface. Этот интерфейс обязывает конкретный класс реализовывать методы, аналогичные callback-функциям. Мы настоятельно рекомендуем использовать объектно ориентированный подход. Он более гибок и современен.

class UserSessionHandler implements SessionHandlerInterface {
    /**
    * Инициализация новой сессии.
    */
    public function open($savePath, $sessionName)
    {
        echo "
            ***************************************<br>
            Открываем новую сессию<br>
            Директория хранения: {$savePath}<br>
            Имя cookie: {$sessionName}<br>
            ------<br>
        ";

        return true;
    }


    /**
    * Обработка закрытия текущей сессии.
    */
    public function close()
    {
        echo "
            Закрываем сессию<br>
            ***************************************<br>
        ";

        return true;
    }


    /**
    * Чтение данных при открытии сессии.
    */
    public function read($sessionId)
    {
        echo "
            Получаем данные сессии #{$sessionId}<br>
            ------<br>
        ";

        return '';
    }


    /**
    * Запись новых пользовательских данных.
    */
    public function write($sessionId, $data)
    {
        echo "
            Записываем данные в сессию #{$sessionId}<br>
            Новое содержимое: {$data}<br>
            ------<br>
        ";

        return true;
    }


    /**
    * Обработка уничтожения сесссии.
    */
    public function destroy($sessionId)
    {
        echo "
            Уничтожаем сессию #{$sessionId}<br>
            ------<br>
        ";

        return true;
    }


    /**
    * Удаление просроченных сессий.
    */
    public function gc($maxleftTime)
    {
        //пока оставим метод пустым
    }
}


//регистрируем обработчики и стартуем
session_set_save_handler(new UserSessionHandler());
session_start();

$_SESSION['newData'] = 'Данные обработаны!!!';

Заметка
Методы write() и read() принимают и возвращают сериализованные строки данных. Обратите внимание, что формат сериализации должен соответствовать опции session.serialize_handler конфигурационного файла php.ini.

Хранение сессий в MySQL

Зачастую для централизованного и эффективного хранения пользовательских сессий используется система управления базами данных MySQL. Мы уже ознакомились с механизмом интеграции в PHP различных хранилищ. Применение MySQL не требует особых усилий. Для этого нам понадобится база данных, которую можно создать с помощью следующего SQL-запроса.

CREATE TABLE session_storage (
    id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(26),
    data TEXT,
    update_time INT(11)
)

Обработку сессионных операций можно реализовать с помощью приведенного ниже механизма. Он не претендует на роль лучшего обработчика хранения сессий. Его целью является раскрытие основных принципов использования MySQL для текущей задачи.

class UserSessionHandler implements SessionHandlerInterface {

    protected $connection;


    /**
    * Создаем подключение к базе данных.
    */
    public function open($savePath, $sessionName)
    {
        try {
            $this->connection = new PDO(
                'mysql:host=127.0.0.1;dbname=coder-booster',
                'root-remote',
                'Tapra45@lo!6'
            );

            return true;

        } catch(PDOException $exception) {
            echo $exception->getMessage();
            return false;
        }
    }


    /**
    * Чтение данных при открытии сессии.
    */
    public function read($sessionId)
    {
        //пытаемся найти уже существующую сессию
        $statement = $this->connection->prepare("
            SELECT * FROM `session_storage`
                WHERE `name` = :name
        ");
        $statement->bindParam(':name', $sessionId);
        $statement->setFetchMode(PDO::FETCH_ASSOC);
        $statement->execute();

        $result = $statement->fetch();

        //если сессия не найдена создадим новую
        if ($result === false) {
            $statement = $this->connection->prepare('
                INSERT INTO `session_storage` (`name`, `update_time`)
                    VALUES (:name, :time)
            ');
            $statement->bindParam(':name', $sessionId);
            $statement->bindParam(':time', time());
            $statement->execute();

            return '';

        //иначе обновим её последнее время доступа
        } else {
            $statement = $this->connection->prepare('
                UPDATE `session_storage` SET `update_time` = :time
                    WHERE `name` = :name
            ');
            $statement->bindParam(':name', $sessionId);
            $statement->bindParam(':time', time());
            $statement->execute();

            return is_string($result['data']) ? $result['data'] : '';
        }
    }


    /**
    * Запись новых пользовательских данных.
    */
    public function write($sessionId, $data)
    {
        $statement = $this->connection->prepare('
            UPDATE `session_storage` SET `update_time` = :time, `data` = :data
                WHERE `name` = :name
        ');
        $statement->bindParam(':name', $sessionId);
        $statement->bindParam(':data', $data);
        $statement->bindParam(':time', time());

        return $statement->execute();
    }


    /**
    * Обработка уничтожения сесссии.
    */
    public function destroy($sessionId)
    {
        $statement = $this->connection->prepare('
            DELETE FROM `session_storage` WHERE `name` = :name
        ');
        $statement->bindParam(':name', $sessionId);

        return $statement->execute();
    }


    /**
    * Удаление просроченных сессий.
    */
    public function gc($maxleftTime)
    {
        $statement = $this->connection->prepare('
            DELETE FROM `session_storage` WHERE `update_time` + :maxTime < :time
        ');
        $statement->bindParam(':maxTime', $maxleftTime);
        $statement->bindParam(':time', time());

        return $statement->execute();
    }


    /**
    * Закрываем подключение к базе данных.
    */
    public function close()
    {
        //никак не обрабатываем закрытие.
        return true;
    }
}

//регистрируем обработчики и стартуем
session_set_save_handler(new UserSessionHandler());
session_start();

$_SESSION['newData'] = 'Данные обработаны!!!';

Последние публикации