Session 是 PHP 應用中維持用戶狀態的核心機制。雖然日常開發中我們習慣直接使用 $_SESSION,但了解其底層運作原理——從配置參數、垃圾回收、ID 傳遞、數據存儲到銷毀流程——對於排查疑難雜症、優化性能以及實現自定義存儲至關重要。本文將依據 PHP 官方手冊與實踐經驗,系統梳理 Session 的完整生命周期。
PHP 的 Session 行為主要由 php.ini 中的以下指令控制:
files,表示以文件形式存儲會話數據;也可設為 user 以啟用自定義處理器。gc_probability / gc_divisor。例如 1/1000 表示每 1000 次請求約觸發 1 次 GC。部分系統(如 Debian 系)將概率設為 0,改由系統 cron 定時清理。PHPSESSID。lifetime)、路徑 (path)、域 (domain)、安全標誌 (secure)、HttpOnly (httponly) 等屬性。session_start() 是會話機制的入口,執行流程如下:
$_COOKIE[session_name()](即 $_COOKIE['PHPSESSID'])。setcookie() 發送至客戶端。注意:此時已發送 HTTP Header,之前不能有任何輸出。SESS_ 為文件名,在 save_path 下尋找文件,讀取內容並反序列化填充 $_SESSION 超全局變量。此階段也會根據概率啟動垃圾回收(掃描文件修改時間並刪除過期文件)。若使用 session_regenerate_id() 重新生成 ID,同樣要求在此之前無輸出。
對 $_SESSION['key'] = 'value' 的操作僅在內存中進行,不會即時寫入文件。這意味著同一請求內多次修改同一鍵,只有最終值會被持久化。
腳本正常結束或顯式調用 session_write_close() 時,PHP 將 $_SESSION 序列化並寫入對應的 Session 文件(若文件不存在則創建)。此過程會獲得文件獨佔鎖,防止併發寫入衝突。
典型案例:用戶登錄前為匿名 Session,登錄後需切換到新 ID 以防會話固定攻擊。常見做法:
if (isset($_COOKIE[session_name()])) {
setcookie(session_name(), '', time() - 42000, '/'); // 使舊 Cookie 過期
}
session_regenerate_id(true); // 參數 true 表示同時刪除舊會話文件
瀏覽器關閉時即時 Cookie 自然失效,但「退出登錄」等業務需主動銷毀:
unset($_SESSION):清空內存中的 $_SESSION,但 Session 文件與 ID 仍保留,下次請求攜帶同 ID 時會讀到空數據。session_destroy():徹底銷毀——刪除 $_SESSION、Session 文件及 Session ID。之後請求若攜帶舊 ID,將找不到數據並重新生成新會話。setcookie(session_name(), session_id(), time() - 8000000, ...)。通過 session_set_save_handler() 註冊六個回調函數,完全接管會話存儲邏輯(如存入 Redis、MySQL、MongoDB):
session_set_save_handler(
'open', // 打開存儲連接
'close', // 關閉存儲連接
'read', // 讀取數據 (參數: $id)
'write', // 寫入數據 (參數: $id, $data)
'destroy',// 銷毀會話 (參數: $id)
'gc' // 垃圾回收 (參數: $maxlifetime)
);
執行順序:session_start() → open() → read() → 腳本運行 → 結束 → write() → close()。調用 session_destroy() 時先執行 destroy() 再走寫入流程。
function open($save_path, $session_name) {
global $sess_save_path;
$sess_save_path = $save_path;
return true;
}
function close() { return true; }
function read($id) {
global $sess_save_path;
$file = "$sess_save_path/sess_$id";
return (string) @file_get_contents($file);
}
function write($id, $sess_data) {
global $sess_save_path;
$file = "$sess_save_path/sess_$id";
$fp = @fopen($file, "w");
if ($fp) {
$ret = fwrite($fp, $sess_data);
fclose($fp);
return $ret;
}
return false;
}
function destroy($id) {
global $sess_save_path;
$file = "$sess_save_path/sess_$id";
return @unlink($file);
}
function gc($maxlifetime) {
global $sess_save_path;
foreach (glob("$sess_save_path/sess_*") as $file) {
if (filemtime($file) + $maxlifetime < time()) {
@unlink($file);
}
}
return true;
}
echo、空白字符、BOM 都會導致 Header 發送失敗。session_write_close() 釋放鎖,允許同一用戶的並發請求並行。save_handler = user。session.cookie_httponly = 1、session.cookie_secure = 1 (HTTPS)、session.use_strict_mode = 1。gc_probability = 0),改用 cron 定時掃描刪除過期文件。掌握 Session 的完整生命周期、配置參數交互關係以及自定義存儲擴展點,能幫助開發者從容應對會話丟失、併發衝突、分佈式一致性等實戰難題。希望本文能成為你排查 Session 問題時的「速查手冊」。