PHP session 写入数据库中的方法

首先解释下为什么要把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 放到服务器 共用的地方.