Разработчику. Сборник рецептов PHP
Задавайте вопросы

Хранение сеансов в базе данных

Вернуться назад

Задача

Необходимо хранить данные сеанса не в файле, а в базе данных. Если несколько веб-серверов используют одну и ту же базу данных, то данные сеанса доступны на всех этих веб-серверах.

Решение

Установите опцию session.save_handler в значение user в файле php.ini и используйте класс pc_DB_Session, Например:

$s = new pc_DB_Session(' mysql://user:password@localhost/db ');
ini_get(' session.auto_start ') or session_start();

Обсуждение

Одна из наиболее сильных сторон модуля сеанса состоит в том, что он способен абстрагировать способ сохранения сеансов. Функция session_set_save_handler() указывает PHP, что различные операции с сеансами, такие как запись сеанса и чтение данных сеанса, следует организовывать посредством разных функций. Класс pc_DB_Session хранит информацию о сеансе в базе данных. Если с этой базой данных совместно работают несколько веб-серверов, то пользовательская информация о сеансе становится доступной на всех этих веб-серверах. Поэтому, если есть группа серверов, находящихся за узлом, регулирующим загрузку, то нет необходимости в каких-либо хитрых уловках для обеспечения корректности пользовательских данных о сеансе, независимо от того, какому серверу они были посланы.

Чтобы использовать pc_DB_Session, передайте имя источника данных (DSN, data source name) классу при его реализации. Данные сеанса сохраняются в таблице с именем php_session, которая имеет следующую структуру:

CREATE TABLE php_session (
     id CHAR(32) NOT NULL,
     data MEDIUMBLOB,
     last_access INT UNSIGNED NOT NULL,
     PRIMARY KEY(id)
)

Если требуется имя таблицы, отличное от php_session, установите значение опции session.save_path в файле php.ini в соответствии с новым именем таблицы. Пример, описанный ниже, демонстрирует класс pc_DB_Session.

Класс pc_DB_Session

require 'PEAR.php';
require 'DB.php';

class pc_DB_Session extends PEAR {
     var $_dbh;
     var $_table;
     var $_connected = false;
     var $_gc_maxlifetime;
     var $_prh_read;
     var $error = null;

/**
* Конструктор
*/
          function pc_DB_Session($dsn = null) {
               if (is_null($dsn)) {
                    $this -> error = PEAR::raiseError('No DSN specified');
                    return;
               }
               $this -> _gc_maxlifetime = ini_get('session.gc_maxlifetime');
               // Сеанс продолжается один день, если не определено другое
          if (! $this -> _gc_maxlifetime) {
               $this -> _gc_maxlifetime = 86400;

          }
          $this -> _table = ini_get('session.save_path');
          if ((! $this -> _table) || ('/tmp' == $this -> _table)) {
               $this -> _table = 'php_session';

          }

          $this -> _dbh = DB::connect($dsn);
          if (DB::isError($this -> _dbh)) {
               $this -> error = $this -> _dbh;
               return;

}

          $this -> _prh_read = $this -> _dbh -> prepare(
               "SELECT data FROM $this -> _table WHERE id LIKE ?
                    AND last_access >= ?");
          if (DB::isError($this -> _prh_read)) {
               $this -> error = $this -> _prh_read;
               return;
          }

     if (! session_set_save_handler(array(&$this,'_open'),
               array(&$this,'_close'),
               array(&$this,'_read'),
               array(&$this,'_write'),
               array(&$this,'_destroy'),
               array(&$this,'_gc'))) {
     $this -> error = PEAR::raiseError('session_set_save_handler()
               failed');
     return;
     }
     return $this -> _connected = true;
     }

     function _open() {
          return $this -> _connected;
}

          function _close() {
               return $this -> _connected;
}

          function _read($id) {
               if (! $this -> _connected) { return false; }
               $sth =
                    $this ->_dbh -> execute($this -> _prh_read,
                    array($id,time() - $this -> _gc_maxlifetime));
          if (DB::isError($sth)) {
               $this -> error = $sth;
               return '';
          } else {
     if (($sth -> numRows() == 1) &&
          ($ar = $sth -> fetchRow(DB_FETCHMODE_ORDERED))) {
          return $ar[0];
     } else {
          return '';
               }
          }
     }

function _write($id,$data) {
     $sth = $this-> _dbh -> query(
          "REPLACE INTO $this -> _table (id,data,last_access) VALUES (?,?,?)",
          array($id,$data,time()));
     if (DB::isError($sth)) {
          $this -> error = $sth;
          return false;
     } else {
          return true;
     }
}

function _destroy($id) {
     $sth = $this -> _dbh -> query("DELETE FROM $this -> _table WHERE id LIKE ?",
          array($id));
     if (DB::isError($sth)) {
          $this -> error = $sth;
          return false;
     } else {
          return true;
     }
}

function _gc($maxlifetime) {
     $sth = $this->_dbh->query("DELETE FROM $this->_table
               WHERE last_access < ?",
               array(time() - $maxlifetime));
     if (DB::isError($sth)) {
          $this->error = $sth;
          return false;
     } else {
          return true;
          }
     }
}

Метод pc_DB_Session::_write() использует MySQL-специфическую команду SQL, REPLACE INTO, которая обновляет существующую запись или вставляет новую, в зависимости от того, есть ли уже в базе данных запись с этим идентификатором поля. Если вы используете другую базу данных, модифицируйте функцию write() для выполнения той же самой задачи. Например, удаляем существующую строку (если она есть) и вставляем новую, выполняя все это в одной транзакции:

function _write($id,$data) {
     $sth = $this ->_dbh -> query('BEGIN WORK');
     if (DB::isError($sth)) {
          $this -> error = $sth;
          return false;

}

     $sth = $this -> _dbh -> query("DELETE FROM $this -> _table WHERE id LIKE ?",
               array($id));
     if (DB::isError($sth)) {
          $this -> error = $sth;
          $this -> _dbh -> query('ROLLBACK');
          return false;
     }

$sth = $this -> _dbh -> query(
          "INSERT INTO $this -> _table (id,data,last_access) VALUES (?,?,?)",
          array($id,$data,time()));
     if (DB::isError($sth)) {
          $this -> error = $sth;
          $this -> _dbh -> query('ROLLBACK');
          return false;

}

$sth = $this -> _dbh -> query('COMMIT');
if (DB::isError($sth)) {
     $this -> error = $sth;
     $this -> _dbh -> query('ROLLBACK');
     return false;
}

          return true;
}

См. также

Документацию по функции session_set_save_handler() на http://www.php.net/session-set-save-handler; информацию об обработчике, использующем PostgreSQL, на http://www.zend.com/codex.php?id=456&single=1;

Вернуться назад

Рейтинг@Mail.ru

Яндекс.Метрика

Индекс цитирования


Рейтинг Сайтов ДОСКИ.РУ