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

Discuz! X Session 機制深度解析:從 Cookie 到內存表的完整鏈路

     編輯:PHP 綜合知識

核心架構

Discuz! X 放棄 PHP 原生 $_SESSION,自建雙表 Session 機制,所有數據存儲於內存表,實現高性能讀寫:

表名用途存儲引擎
pre_common_session前台用戶瀏覽會話MEMORY
pre_common_adminsession後台管理員會話MEMORY

登錄流程與 Session 建立

1. 入口分發

// member.php
$mod = 'logging'; // 根據參數確定
require DISCUZ_ROOT . './source/module/member/member_' . $mod . '.php';

2. 控制器調度

// source/module/member/member_logging.php
$ctl = new logging_ctl();
$method = 'on_' . $_G['gp_action']; // 'on_login'
$ctl->$method();

3. 登錄驗證與狀態設置

// logging_ctl::on_login()
if (submitcheck('loginsubmit')) {
    $result = userlogin($username, $password, ...); // 驗證用戶
    if ($result['status'] > 0) {
        setloginstatus($result['member'], $cookietime); // 關鍵:寫 Cookie + 標記 Session 新建
    }
}

4. setloginstatus 核心邏輯

// source/function/function_login.php
function setloginstatus($member, $cookietime) {
    global $_G;
    $_G['uid'] = $member['uid'];
    $_G['username'] = $member['username'];
    // ... 其他用戶信息賦值 ...
    
    $_G['formhash'] = formhash();
    $_G['core']->session->isnew = 1; // 標記需要創建新 Session
    
    // 寫入加密 Cookie
    dsetcookie('auth', authcode("{$member['password']}\t{$member['uid']}", 'ENCODE'), $cookietime);
    dsetcookie('loginuser', $member['username'], $cookietime);
    // ... 其他 Cookie ...
}

Session 類核心實現

文件:source/class/class_core.php → discuz_session 類

1. 創建新 Session

function create($ip, $uid) {
    $this->isnew = true;
    $this->var = $this->newguest; // 默認游客結構
    $this->set('sid', random(6));       // 6位隨機 Session ID
    $this->set('uid', $uid);
    $this->set('ip', $ip);
    $this->set('lastactivity', TIMESTAMP);
    $this->sid = $this->var['sid'];
    return $this->var;
}

2. 更新/持久化 Session

function update() {
    if ($this->sid !== null) {
        $data = daddslashes($this->var);
        if ($this->isnew) {
            $this->delete(); // 刪除舊同 SID 記錄
            DB::insert('common_session', $data, false, false, true); // REPLACE INTO
        } else {
            DB::update('common_session', $data, "sid='$data[sid]'");
        }
        dsetcookie('sid', $this->sid, 86400); // 同步 Cookie 中的 SID
    }
}

請求生命周期中的 Session 載入與保存

1. 核心類初始化載入

// discuz_core::_init_session()
$this->session = new discuz_session();
$this->session->init($_G['cookie']['sid'], $_G['clientip'], $_G['uid']);

$_G['sid'] = $this->session->sid;
$_G['session'] = $this->session->var;

// SID 不一致(如多用戶共用瀏覽器)重新寫 Cookie
if ($_G['sid'] != $_G['cookie']['sid']) {
    dsetcookie('sid', $_G['sid'], 86400);
}

// 新 Session 或超時更新用戶活動時間
if ($_G['uid'] && ($this->session->isnew || ($this->session->get('lastactivity') + 600) < TIMESTAMP)) {
    $this->session->set('lastactivity', TIMESTAMP);
    DB::update('common_member_status', [
        'lastip' => $_G['clientip'],
        'lastactivity' => TIMESTAMP,
        'lastvisit' => $this->session->isnew ? TIMESTAMP : null
    ], "uid='$_G[uid]'");
}

2. 模板觸發持久化

關鍵點:模板編譯後的 PHP 文件中埋點

// 幾乎所有模板文件底部
{eval updatesession();}
// source/function/function_core.php
function updatesession($force = false) {
    global $_G;
    static $updated = false;
    if (!$updated) {
        $discuz = discuz_core::instance();
        // 同步用戶組、操作信息到 Session
        foreach ($_G['member'] as $k => $v) if ($k != 'lastactivity') $discuz->session->set($k, $v);
        foreach ($_G['action'] as $k => $v) $discuz->session->set($k, $v);
        $discuz->session->update(); // 真正寫入內存表
        $updated = true;
    }
    return $updated;
}

完整鏈路時序圖

用戶提交登錄表單
       │
       ▼
member.php → logging_ctl::on_login()
       │
       ▼
userlogin() 驗證通過
       │
       ▼
setloginstatus() 
  ├─ 賦值 $_G 用戶信息
  ├─ session->isnew = 1
  └─ dsetcookie('auth', ...)  ← 瀏覽器獲得 Cookie
       │
       ▼
瀏覽器自動攜帶 Cookie 重定向/刷新
       │
       ▼
discuz_core::init() → _init_session()
  ├─ 讀取 Cookie sid
  ├─ session->init() 載入/創建 Session 對象
  └─ 若 SID 變更 → 更新 Cookie sid
       │
       ▼
執行業務邏輯...
       │
       ▼
模板輸出 {eval updatesession()}
       │
       ▼
updatesession() → session->update()
       │
       ▼
寫入/更新 pre_common_session 內存表
       │
       ▼
請求結束

關鍵設計亮點

設計優勢
內存表存儲讀寫性能極高,適合高併發 Session 訪問
Cookie + Server 雙存Cookie 攜帶 SID 快速定位,Server 存儲完整數據
模板埋點持久化確保頁面渲染完成後統一寫入,避免中間狀態污染
isnew 標記機制區分首次創建與更新,優化數據庫操作
超時自動更新 lastactivity600 秒閾值平衡準確性與寫入頻率

常見問題排查

  • Session 丟失:檢查 Cookie 域/路徑設置、內存表是否滿、SID 是否被篡改
  • 並發衝突:內存表無行鎖,極高併發下可能覆蓋,可考慮 Redis 替代
  • 跨子域共享:設置 Cookie domain 為 .example.com,確保 SID 傳遞
  • 管理員後台無法登錄:檢查 pre_common_adminsession 表結構、IP 白名單

現代化改造建議

  1. 引入 Redis Cluster 替換內存表,支持多機部署
  2. Session 數據結構化(JSON),便於異構語言共享
  3. 引入 JWT 無狀態認證作為補充,減少 Session 查詢
  4. 增加 Session 活躍度監控、異常登錄告警
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved