Discuz! X 放棄 PHP 原生 $_SESSION,自建雙表 Session 機制,所有數據存儲於內存表,實現高性能讀寫:
| 表名 | 用途 | 存儲引擎 |
|---|---|---|
| pre_common_session | 前台用戶瀏覽會話 | MEMORY |
| pre_common_adminsession | 後台管理員會話 | MEMORY |
// member.php $mod = 'logging'; // 根據參數確定 require DISCUZ_ROOT . './source/module/member/member_' . $mod . '.php';
// source/module/member/member_logging.php $ctl = new logging_ctl(); $method = 'on_' . $_G['gp_action']; // 'on_login' $ctl->$method();
// logging_ctl::on_login()
if (submitcheck('loginsubmit')) {
$result = userlogin($username, $password, ...); // 驗證用戶
if ($result['status'] > 0) {
setloginstatus($result['member'], $cookietime); // 關鍵:寫 Cookie + 標記 Session 新建
}
}
// 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 ...
}
文件:source/class/class_core.php → discuz_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;
}
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
}
}
// 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]'");
}
關鍵點:模板編譯後的 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 標記機制 | 區分首次創建與更新,優化數據庫操作 |
| 超時自動更新 lastactivity | 600 秒閾值平衡準確性與寫入頻率 |
pre_common_adminsession 表結構、IP 白名單