使用 session_set_save_handler() 註冊自定義儲存處理器,將 Session 資料存入 MySQL 而非預設檔案系統,支援分散式部署、叢集環境共享 Session。
class SessionHandler {
protected $maxlifetime = null;
protected $link = null;
public $config = null;
public static function init($args) { return new self($args); }
public function __construct($args) {
$this->config = $args;
$this->maxlifetime = get_cfg_var("session.gc_maxlifetime");
session_set_save_handler(
[$this, "open"],
[$this, "close"],
[$this, "read"],
[$this, "write"],
[$this, "destroy"],
[$this, "gc"]
);
}
// 開啟連線、建立資料表
public function open() {
$this->link = mysqli_connect(
$this->config['host'],
$this->config['user'],
$this->config['password'],
$this->config['database']
);
mysqli_set_charset($this->link, "utf8");
$sql = 'CREATE TABLE IF NOT EXISTS `%s` (
`session_id` varchar(255) NOT NULL,
`session_data` text,
`session_expires` char(10) NOT NULL,
PRIMARY KEY (`session_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;';
$sql = sprintf($sql, $this->config['table']);
mysqli_query($this->link, $sql);
return true;
}
public function close() { return true; }
// 讀取 Session
public function read($session_id) {
if (empty($session_id)) return null;
$sql = 'SELECT `session_data` AS `data` FROM `%s`
WHERE `session_id` = "%s" AND `session_expires` > "%u"';
$sql = sprintf($sql,
mysqli_real_escape_string($this->link, $this->config['table']),
mysqli_real_escape_string($this->link, $session_id),
time()
);
$result = mysqli_query($this->link, $sql);
$row = mysqli_fetch_assoc($result);
return $row['data'] ?? '';
}
// 寫入 Session (REPLACE INTO 實現 upsert)
public function write($session_id, $session_data) {
if (empty($session_id)) return null;
$newExpires = time() + $this->maxlifetime;
$sql = 'REPLACE INTO `%s` SET
`session_id` = "%s",
`session_data` = "%s",
`session_expires` = "%u"';
$sql = sprintf($sql,
mysqli_real_escape_string($this->link, $this->config['table']),
mysqli_real_escape_string($this->link, $session_id),
mysqli_real_escape_string($this->link, $session_data),
$newExpires
);
$result = mysqli_query($this->link, $sql);
return mysqli_affected_rows($this->link);
}
// 銷毀 Session
public function destroy($session_id) {
$sql = 'DELETE FROM `%s` WHERE `session_id` = "%s"';
$sql = sprintf($sql,
mysqli_real_escape_string($this->link, $this->config['table']),
mysqli_real_escape_string($this->link, $session_id)
);
$result = mysqli_query($this->link, $sql);
return mysqli_affected_rows($this->link);
}
// 垃圾回收:清理過期 Session
public function gc() {
$sql = 'DELETE FROM `%s` WHERE `session_expires` < "%u"';
$sql = sprintf($sql,
mysqli_real_escape_string($this->link, $this->config['table']),
time()
);
$result = mysqli_query($this->link, $sql);
return mysqli_affected_rows($this->link);
}
}
class Session {
public static $collection = null;
public static function open($clean = false, $token = false) {
if ($clean) ob_end_clean();
if ($token) session_id($token);
session_start();
self::$collection = $_SESSION;
}
public static function id() {
$args = func_get_args();
return $args ? session_id($args[0]) : session_id();
}
public static function get($name) {
return isset($_SESSION[$name]) ? $_SESSION[$name] : null;
}
public static function set($name, $value) {
$_SESSION[$name] = $value;
return true;
}
public static function delete($name) {
if (!isset($_SESSION[$name])) return null;
unset($_SESSION[$name]);
return true;
}
public static function destroy() { session_destroy(); }
}
$config = [
"host" => "127.0.0.1",
"user" => "root",
"password" => "123456",
"database" => "test",
"charset" => "utf8",
"table" => "user_session"
];
SessionHandler::init($config);
Session::open();
Session::set("profile", ["id" => 1, "user" => "haowei", "vip-level" => 6]);
| 欄位 | 型別 | 說明 |
|---|---|---|
session_id | VARCHAR(255) | 主鍵,PHP Session ID |
session_data | TEXT | 序列化後的 Session 資料 |
session_expires | CHAR(10) | Unix 時間戳,過期時間 |
session_expires 加入索引加速 GC 查詢gzcompress() 壓縮存儲prepare() 防注入