首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 相關知識

PHP 4 Session 處理器擴充教學:DBM 與 MySQL 儲存後端實作

     編輯:PHP 相關知識

注意:本文內容基於 PHP 4 (2000 年),僅供歷史參考。現代 PHP (5.4+) 內建 SessionHandlerInterface,請參考官方文件實作自定儲存器。

PHP 4 預設將 Session 存為檔案 (/tmp/sess_...),分散式部署或高併發時易遇到:檔案系統 inode 耗盡、NFS 鎖效能差、多伺服器無法共享 Session。解決方案是實作 session_set_save_handler() 註冊的 6 個回調函式,將資料導向 DBM 檔案或 MySQL 資料庫。

核心回調函式簽名

函式參數職責
sess_open$save_path, $session_name初始化連線 (開啟 DBM/DB 連線)
sess_close無關閉連線 (DBM 需關閉,MySQL 持久連線可空)
sess_read$key (session_id)讀取資料並回傳字串 (需檢查過期)
sess_write$key, $val (序列化資料)寫入資料 (附加過期時間戳)
sess_destroy$key刪除指定 session
sess_gc$maxlifetime垃圾回收:刪除過期 session

方案一:DBM 檔案儲存 (session_dbm.php)

<?php
// session_dbm.php - PHP 4 DBM Session Handler
$SESS_DBM = '';
$SESS_LIFE = get_cfg_var('session.gc_maxlifetime');

function sess_open($save_path, $session_name) {
    global $SESS_DBM;
    $SESS_DBM = dbmopen("$save_path/$session_name", 'c'); // 'c' = create if not exist
    return $SESS_DBM;
}

function sess_close() {
    global $SESS_DBM;
    dbmclose($SESS_DBM);
    return true;
}

function sess_read($key) {
    global $SESS_DBM, $SESS_LIFE;
    if ($tmp = dbmfetch($SESS_DBM, $key)) {
        // 格式: expiry|data
        list($expiry, $data) = explode('|', $tmp, 2);
        if ($expiry > time()) return $data;
    }
    return '';
}

function sess_write($key, $val) {
    global $SESS_DBM, $SESS_LIFE;
    $expiry = time() + $SESS_LIFE;
    dbmreplace($SESS_DBM, $key, "$expiry|$val");
    return true;
}

function sess_destroy($key) {
    global $SESS_DBM;
    dbmdelete($SESS_DBM, $key);
    return true;
}

function sess_gc($maxlifetime) {
    global $SESS_DBM;
    $now = time();
    $key = dbmfirstkey($SESS_DBM);
    while ($key) {
        if ($tmp = dbmfetch($SESS_DBM, $key)) {
            $expiry = (int)substr($tmp, 0, strpos($tmp, '|'));
            if ($now > $expiry) sess_destroy($key);
        }
        $key = dbmnextkey($SESS_DBM, $key);
    }
}

session_set_save_handler('sess_open', 'sess_close', 'sess_read', 'sess_write', 'sess_destroy', 'sess_gc');
?>

方案二:MySQL 資料庫儲存 (session_mysql.php)

建表 SQL

CREATE DATABASE sessions;
GRANT SELECT,INSERT,UPDATE,DELETE ON sessions.* TO 'phpsession'@'localhost' IDENTIFIED BY 'phpsession';
USE sessions;
CREATE TABLE sessions (
    sesskey CHAR(32) NOT NULL,
    expiry INT(11) UNSIGNED NOT NULL,
    value TEXT NOT NULL,
    PRIMARY KEY (sesskey)
);

Handler 實作

<?php
// session_mysql.php - PHP 4 MySQL Session Handler
$SESS_DBHOST = 'localhost';
$SESS_DBNAME = 'sessions';
$SESS_DBUSER = 'phpsession';
$SESS_DBPASS = 'phpsession';
$SESS_DBH = '';
$SESS_LIFE = get_cfg_var('session.gc_maxlifetime');

function sess_open($save_path, $session_name) {
    global $SESS_DBHOST, $SESS_DBNAME, $SESS_DBUSER, $SESS_DBPASS, $SESS_DBH;
    $SESS_DBH = mysql_pconnect($SESS_DBHOST, $SESS_DBUSER, $SESS_DBPASS);
    if (!$SESS_DBH) die('DB Connect Failed: ' . mysql_error());
    mysql_select_db($SESS_DBNAME, $SESS_DBH);
    return true;
}

function sess_close() { return true; } // 持久連線不關閉

function sess_read($key) {
    global $SESS_DBH, $SESS_LIFE;
    $key = addslashes($key);
    $qry = "SELECT value FROM sessions WHERE sesskey='$key' AND expiry > " . time();
    $res = mysql_query($qry, $SESS_DBH);
    if ($row = mysql_fetch_row($res)) return $row[0];
    return '';
}

function sess_write($key, $val) {
    global $SESS_DBH, $SESS_LIFE;
    $key = addslashes($key);
    $val = addslashes($val);
    $expiry = time() + $SESS_LIFE;
    // 嘗試 INSERT,失敗則 UPDATE (主鍵衝突)
    $qry = "INSERT INTO sessions (sesskey, expiry, value) VALUES ('$key', $expiry, '$val')";
    if (!mysql_query($qry, $SESS_DBH)) {
        $qry = "UPDATE sessions SET expiry=$expiry, value='$val' WHERE sesskey='$key' AND expiry > " . time();
        mysql_query($qry, $SESS_DBH);
    }
    return true;
}

function sess_destroy($key) {
    global $SESS_DBH;
    $key = addslashes($key);
    mysql_query("DELETE FROM sessions WHERE sesskey='$key'", $SESS_DBH);
    return true;
}

function sess_gc($maxlifetime) {
    global $SESS_DBH;
    mysql_query("DELETE FROM sessions WHERE expiry < " . time(), $SESS_DBH);
    return mysql_affected_rows($SESS_DBH);
}

session_set_save_handler('sess_open', 'sess_close', 'sess_read', 'sess_write', 'sess_destroy', 'sess_gc');
?>

使用方式

<?php
// test.php
$handler = $_GET['handler'] ?? 'dbm'; // dbm 或 mysql
include "session_$handler.php";       // 註冊 handler
session_start();
session_register('count');            // PHP 4 風格

$action = $_GET['action'] ?? 'increment';
switch ($action) {
    case 'increment': $count = ($count ?? 0) + 1; break;
    case 'destroy': session_destroy(); break;
    case 'gc': sess_gc(get_cfg_var('session.gc_maxlifetime')); break;
}
?>
<h1>Session Test</h1>
<p>Handler: <b><?=$handler?></b> | Action: <b><?=$action?></b> | Count: <b><?=$count?></b></p>
<form>
  <select name="handler"><option value="dbm">DBM</option><option value="mysql">MySQL</option></select>
  <select name="action"><option value="increment">Increment</option><option value="destroy">Destroy</option><option value="gc">GC</option></select>
  <input type="submit">
</form>

現代 PHP (7.0+) 實作對照

<?php
// 現代寫法:實作 SessionHandlerInterface
class MySqlSessionHandler implements SessionHandlerInterface {
    private $pdo;
    public function __construct(PDO $pdo) { $this->pdo = $pdo; }
    public function open($savePath, $sessionName) { return true; }
    public function close() { return true; }
    public function read($id) {
        $stmt = $this->pdo->prepare('SELECT data FROM sessions WHERE id=? AND expiry>?');
        $stmt->execute([$id, time()]);
        return $stmt->fetchColumn() ?: '';
    }
    public function write($id, $data) {
        $expiry = time() + ini_get('session.gc_maxlifetime');
        $stmt = $this->pdo->prepare('INSERT INTO sessions (id,expiry,data) VALUES (?,?,?) ON DUPLICATE KEY UPDATE expiry=?,data=?');
        return $stmt->execute([$id, $expiry, $data, $expiry, $data]);
    }
    public function destroy($id) {
        $stmt = $this->pdo->prepare('DELETE FROM sessions WHERE id=?');
        return $stmt->execute([$id]);
    }
    public function gc($maxlifetime) {
        $stmt = $this->pdo->prepare('DELETE FROM sessions WHERE expiryexecute([time()]);
    }
}

// 註冊
$handler = new MySqlSessionHandler(new PDO('mysql:dbname=sessions;host=localhost', 'user', 'pass'));
session_set_save_handler($handler, true); // true = 使用 OOP 介面
session_start();

總結

  • PHP 4 時代透過 session_set_save_handler + 6 個函式實現儲存後端替換。
  • DBM 適合單機高效能;MySQL 適合分散式叢集共享 Session。
  • 現代 PHP 請改用 SessionHandlerInterface + PDO,支援型別提示、依賴注入、單元測試。
  • 生產環境建議考慮 Redis / Memcached 作為 Session 儲存,效能更佳。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved