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

PHP session_set_save_handler 實現 MySQL 存儲 Session 完整實戰

     編輯:PHP 相關知識

默認 Session 以文件存儲,分布式/集群環境下無法共享。使用 session_set_save_handler() 註冊自定義回調,可將 Session 寫入 MySQL,實現多服務器 Session 共享、在線用戶統計、精準過期控制。

核心類:Session 完整實現

<?php
/**
 * 數據庫 Session 處理器
 * 表結構:sessions(session_id CHAR(32) PK, uid INT, last_visit INT)
 * 過期策略:1 小時無操作視為過期
 */
class Session {
    private $db;
    private $lasttime = 3600; // 過期秒數

    public function __construct(&$db) {
        $this->db = &$db;
        session_module_name('user'); // 必須:聲明使用用戶級處理器
        session_set_save_handler(
            [$this, 'open'],
            [$this, 'close'],
            [$this, 'read'],
            [$this, 'write'],
            [$this, 'destroy'],
            [$this, 'gc']
        );
        session_start(); // 必須在註冊後調用
    }

    // 反序列化 Session 數據(PHP 內部格式)
    private function unserializes($data) {
        $vars = preg_split('/([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)\|/', $data, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE);
        $result = [];
        for ($i = 0; isset($vars[$i]); $i++) {
            $result[$vars[$i++]] = unserialize($vars[$i]);
        }
        return $result;
    }

    // 打開 Session(初始化連接等)
    public function open($path, $name) { return true; }

    // 關閉 Session(腳本結束時觸發)
    public function close() {
        $this->gc($this->lasttime); // 關閉時執行一次 GC
        return true;
    }

    // 讀取 Session 數據
    public function read($sessionId) {
        $sql = "SELECT uid FROM sessions WHERE session_id = '" . $sessionId . "' LIMIT 1";
        $query = $this->db->query($sql);
        if ($row = $this->db->fetch_array($query)) {
            return $row['uid']; // 返回字符串,PHP 會自動反序列化
        }
        return '';
    }

    // 寫入/更新 Session 數據
    public function write($sessionId, $data) {
        // 重新建立 DB 連接(長連接可能已斷開)
        require_once(MRoot . DIR_WS_CLASSES . 'db_mysql_class.php');
        $db1 = new DbCom();
        $db1->connect(DB_SERVER, DB_SERVER_USERNAME, DB_SERVER_PASSWORD, DB_DATABASE);
        $db1->query("SET NAMES utf8");
        $this->db = $db1;

        $data = addslashes($data);
        $arr  = $this->unserializes($data);

        $sql0 = "SELECT uid FROM sessions WHERE session_id = '" . $sessionId . "' LIMIT 1";
        $query0 = $this->db->query($sql0);

        if ($this->db->num_rows($query0) <= 0) {
            // 新 Session:插入記錄
            if (isset($arr['webid']) && !empty($arr['webid'])) {
                $this->db->query("INSERT INTO `sessions` SET `session_id` = '$sessionId', uid = '" . $arr['webid'] . "', last_visit = " . time());
            }
        } else {
            // 存在則可選擇更新 last_visit 或 uid(此處示例略過更新以減少寫入)
        }
        return true;
    }

    // 銷毀 Session(登出調用)
    public function destroy($sessionId) {
        $this->db->query("DELETE FROM `sessions` WHERE `session_id` = '$sessionId'");
        return true;
    }

    // 垃圾回收(清理過期 Session)
    public function gc($lifetime) {
        $this->db->query("DELETE FROM `sessions` WHERE UNIX_TIMESTAMP(NOW()) - `last_visit` > " . $this->lasttime);
        return true;
    }
}
?>

關鍵配置:php.ini 相關參數解讀

參數默認值說明與建議
session.save_handlerfiles改為 user 啟用自定義處理器
session.save_path/tmp文件模式下的存儲路徑;用戶模式下忽略
session.gc_probability1GC 概率分子,建議 1
session.gc_divisor100GC 概率分母,建議 1000~5000 降低頻率
session.gc_maxlifetime1440過期秒數,需與代碼中 $lasttime 一致
session.use_cookiesOn是否用 Cookie 傳 SID
session.use_only_cookiesOff建議 On 防 URL 洩露 SID
session.cookie_httponlyOff建議 On 防 XSS 竊取 SID
session.hash_function0 (MD5)建議 1 (SHA-1) 更安全
session.hash_bits_per_character4建議 5 或 6 縮短 SID 長度

生產環境優化建議

  • 使用預處理語句:防 SQL 注入,將 query() 改為 prepare()/execute()。
  • 連接池/長連接:避免每次 write 重新連接,使用持久連接或單例模式管理 DB 句柄。
  • GC 概率調優:高併發下建議 gc_probability=1, gc_divisor=5000,並配合 cron 定時清理過期數據,避免請求阻塞。
  • Session 鎖機制:默認文件 Session 有鎖防並發衝突;DB 實現需自行處理(如 SELECT ... FOR UPDATE 或 Redis 鎖)。
  • 考慮 Redis:高性能場景建議直接用 session.save_handler = redis(需安裝 phpredis 擴展),無需自寫類。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved