天天看點

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 放到伺服器 共用的地方.

朝朝暮暮.

繼續閱讀