Необходимо хранить данные сеанса не в файле, а в базе данных. Если несколько веб-серверов используют одну и ту же базу данных, то данные сеанса доступны на всех этих веб-серверах.
Установите опцию 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;