建表

既然要把session存入数据库,那首先要有一张存session的表了,我们在test库中创建一张session表

  1. CREATE TABLE `session` (
  2. `session_id` CHAR(32) NOT NULL COMMENT 'sessionid',
  3. `session_data` TEXT NOT NULL COMMENT '数据',
  4. `session_expire` INT(11) NOT NULL COMMENT '过期时间',
  5. `session_expire_data` DATETIME NULL DEFAULT NULL COMMENT '过期时间,日期形式'
  6. )
  7. COLLATE='utf8_general_ci'
  8. ENGINE=MyISAM;

重写session处理函数

有了表之后我们只需要重写session的处理函数就行了

处理函数为 session_set_save_handler(),他有6个参数,后面3个参数为可选参数,不用管。6个参数均为回调函数,下面我们一一来看

第一个参数 open($save_path,$session_name) — open 回调函数类似于类的构造函数, 在会话打开的时候会被调用。 这是自动开始会话或者通过调用 session_start() 手动开始会话 之后第一个被调用的回调函数。 此回调函数操作成功返回 TRUE,反之返回 FALSE。

第二个参数 close() — close 回调函数类似于类的析构函数。 在 write 回调函数调用之后调用。 当调用 session_write_close() 函数之后,也会调用 close 回调函数。 此回调函数操作成功返回 TRUE,反之返回 FALSE。

第三个参数 read($session_id) — 简单的说就是读取数据

第四个参数 write($session_id,$session_data) — 简单的说就是存储数据

第五个参数 destroy($session_id) — 删除数据

第六个参数 gc($expire_time) — 垃圾收集回调函数,用于删除过期数据,但是 调用周期由 session.gc_probability 和 session.gc_divisor 参数控制,默认应该为1/1000,可以把两个参数都修改为1,就是100%了

通过上面的了解后,下面我们直接看代码

<?php
    /**
     * 连接数据库
     */
    function getConnection(){
        $dsn = sprintf("mysql:host=%s;dbname=%s;charset=utf8", 'localhost', 'test');
        $db_user = 'root';
        $db_pass = 'root';
        //指定查询结果集为关联数组
        $option = array(PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC);
        $pdo = new PDO($dsn, $db_user, $db_pass, $option);
        return $pdo;
    }

    /**
     * 启动会话
     */
    function my_session_open($save_path,$session_name){
        return true;
    }
    /**
     * 关闭会话
     */
    function my_session_close(){
        return true;
    }
    /**
     * 读取数据 -- 必须返回 string
     */
    function my_session_read($session_id){
        $pdo = getConnection();
        $sql = 'select * from session where session_id = :session_id and session_expire > :session_expire';
        $sth = $pdo->prepare($sql);
        $sth->bindValue(':session_id',$session_id);
        $sth->bindValue(':session_expire',time());
        $sth->execute();
        $res = $sth->fetch();
        if ($res) {
            return $res['session_data'];
        }
        return '';
    }
    /**
     * 写入数据
     */
    function my_session_write($session_id,$session_data){
        $pdo = getConnection();

        $sql = 'select * from session where session_id = :session_id';
        $sth = $pdo->prepare($sql);
        $sth->bindValue(':session_id',$session_id);
        $sth->execute();
        $res = $sth->fetch();

        $max_time = get_cfg_var('session.gc_maxlifetime');
        $session_expire = time() + $max_time;

        if ($res) {
            //更新数据
            $sql = 'update session set session_data = :session_data,session_expire = :session_expire,session_expire_data = :session_expire_data where session_id = :session_id';
        }else{
            //添加数据
            $sql = 'insert into session (session_id,session_data,session_expire,session_expire_data) values (:session_id,:session_data,:session_expire,:session_expire_data)';
        }
        $sth = $pdo->prepare($sql);
        $sth->bindValue(':session_id',$session_id);
        $sth->bindValue(':session_data',$session_data);
        $sth->bindValue(':session_expire',$session_expire);
        $sth->bindValue(':session_expire_data',date('Y-m-d H:i:s',$session_expire));
        $sth->execute();

        return true;
    }
    /**
     * 删除数据
     */
    function my_session_destroy($session_id){
        $pdo = getConnection();
        $sql = 'delete from session where session_id = :session_id';
        $sth = $pdo->prepare($sql);
        $sth->bindValue(':session_id',$session_id);
        $sth->execute();
        return true;
    }
    /**
     * 删除过期数据
     */
    function my_session_gc($expire_time){
        $pdo = getConnection();
        $sql = 'delete from session where session_expire < :session_expire';
        $sth = $pdo->prepare($sql);
        $sth->bindValue(':session_expire',time());
        $sth->execute();
        return true;
    }

    session_set_save_handler('my_session_open','my_session_close','my_session_read','my_session_write','my_session_destroy','my_session_gc');
    session_start();
    $_SESSION['name'] = '小明';
    echo $_SESSION['name'];

其实也可以使用 php自带的 SessionHandler 类,如

<?php
class Session extends SessionHandler{
    protected $pdo;
    public function __construct(){
        $dsn = sprintf("mysql:host=%s;dbname=%s;charset=utf8", 'localhost', 'test');
        $db_user = 'root';
        $db_pass = 'root';
        //指定查询结果集为关联数组
        $option = array(PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC);
        $this->pdo = new PDO($dsn, $db_user, $db_pass, $option);
    }
    public function open($save_path, $session_name){
        //...
        return true;
    }
    public function close(){
        //...
        return true;
    }
    public function read($session_id){
        //...
        return '';
    }
    public function write($session_id, $session_data){
        //...
        return true;
    }
    public function destroy($session_id){
        //...
        return true;
    }
    public function gc($maxlifetime){
        //...
        return true;
    }
}
session_set_save_handler(new Session());

存Rdis

可以和上面一样,写一个处理类
也可以直接用 ini_set() 函数

<?php
ini_set("session.save_handler", "redis");
ini_set("session.save_path", "tcp://127.0.0.1:6379");
//如果redis设置了密码,可以这样设置
ini_set("session.save_path", "tcp://127.0.0.1:6379?auth=authpwd");

结语

主要就是重新改写那几个回调函数,是不是很简单呢