首先解釋下為什麼要把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 放到伺服器 共用的地方.
朝朝暮暮.