首先解释下为什么要把session 写到数据库中呢,session 一般默认是以文件的形式放在php.ini 配置的目录中的, 如果你的网站实现了多台服务器负载均衡,这样用户访问你的网站,可能进入的服务器就不同,如果没有实现吧session 文件在服务器上实现同步,那么就可能会出现session 丢失问题,最常见的例子就是,我登陆了后台,点着点着 就又让重新登录了,这是个典型的session 没有同步而引起的session 丢失的问题。
解决方案当然有很多,现在介绍一个就是把用户的session 写入到数据库中去,这样session 都从数据库中读取,就不会有丢失的情况发生。
首先要更改php.ini 里面
session.save_handler = files
为:
session.save_handler = user
PHP 有个session_set_save_handler() 函数 ,这个函数就是自定义处理session 的机制,一般 要定义
'open', 'close', 'read', 'write','destroy', 'gc'
这几个函数
下面是个session 写入数据库的一个类
<?php
class SessionToDB
{
private $_path = null;
private $_name = null;
private $_pdo = null;
private $_ip = null;
private $_maxLifeTime = 0; public function __construct(PDO $pdo)
{
//注册处理session 的函数
session_set_save_handler(
array(&$this, 'open'),
array(&$this, 'close'),
array(&$this, 'read'),
array(&$this, 'write'),
array(&$this, 'destroy'),
array(&$this, 'gc')
); $this->_pdo = $pdo;
$this->_ip = !empty($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : null;
$this->_maxLifeTime = ini_get('session.gc_maxlifetime');
} public function open($path,$name)
{
return true;
} public function close()
{
return true;
}
//读取函数
public function read($id)
{
$sql = 'SELECT * FROM session where PHPSESSID = ?';
$stmt = $this->_pdo->prepare($sql);
$stmt->execute(array($id)); if (!$result = $stmt->fetch(PDO::FETCH_ASSOC)) {
return null;
} elseif ($this->_ip != $result['client_ip']) {
return null;
} elseif ($result['update_time']+$this->_maxLifeTime < time()){
$this->destroy($id);
return null;
} else {
return $result['data'];
}
}
//写入函数
public function write($id,$data)
{
$sql = 'SELECT * FROM session where PHPSESSID = ?';
$stmt = $this->_pdo->prepare($sql);
$stmt->execute(array($id)); if ($result = $stmt->fetch(PDO::FETCH_ASSOC)) {
if ($result['data'] != $data) {
$sql = 'UPDATE session SET update_time =? , data = ? WHERE PHPSESSID = ?'; $stmt = $this->_pdo->prepare($sql);
$stmt->execute(array(time(), $data, $id));
}
} else {
if (!empty($data)) {
$sql = 'INSERT INTO session (PHPSESSID, update_time, client_ip, data) VALUES (?,?,?,?)';
$stmt = $this->_pdo->prepare($sql);
$stmt->execute(array($id, time(), $this->_ip, $data));
}
}
return true;
}
//销毁函数
public function destroy($id)
{
$sql = 'DELETE FROM session WHERE PHPSESSID = ?';
$stmt = $this->_pdo->prepare($sql);
$stmt->execute(array($id));
return true;
}
//这个函数执行的几率有php.ini 控制
public function gc($maxLifeTime)
{
$sql = 'DELETE FROM session WHERE update_time < ?';
$stmt = $this->_pdo->prepare($sql);
$stmt->execute(array(time() - $maxLifeTime));
return true;
}
}
//调用方式
ini_set('session.save_handler','user');//更改为user 方式
ini_set('session.gc_maxlifetime', '86400');//session 最大有效期可以不设
$dbname='数据库名称';
$dbuser='用户名';
$dbpwd=密码'';
$dbhost='主机地址';
try{
$pdo = new PDO("mysql:host=$dbhost;dbname=$dbname", $dbuser,$dbpwd);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); new SessionToDB($pdo);
session_start();
} catch(PDOException $e) {
echo 'Error: '.$e->getMessage();
}
?>
数据库结构:
CREATE TABLE `session` (
`PHPSESSID` varchar(50) NOT NULL,
`update_time` int(10) NOT NULL,
`client_ip` varchar(25) NOT NULL,
`data` text NOT NULL,
PRIMARY KEY (`PHPSESSID`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
当然解决这个问题的方法很多,思路就是把session 放到服务器 共用的地方.