getCookieParams(); // 从 Cookie 中获取 Session ID if (isset($cookies[self::SESSION_NAME]) && !empty($cookies[self::SESSION_NAME])) { $sessionId = $cookies[self::SESSION_NAME]; // 验证 Session ID 格式 if (preg_match('/^[a-zA-Z0-9]{32,}$/', $sessionId)) { return $sessionId; } } // 生成新的 Session ID return self::generateSessionId(); } /** * 生成 Session ID * @return string */ private static function generateSessionId(): string { return bin2hex(random_bytes(16)); } /** * 加载 Session 数据 * @param string $sessionId * @return array */ private static function loadSessionData(string $sessionId): array { $filePath = self::getSessionFilePath($sessionId); if (!file_exists($filePath)) { return []; } $data = file_get_contents($filePath); if ($data === false) { return []; } $sessionData = json_decode($data, true); if (!is_array($sessionData)) { return []; } // 检查是否过期 if (isset($sessionData['expires_at']) && $sessionData['expires_at'] < time()) { self::destroySession($sessionId); return []; } return $sessionData['data'] ?? []; } /** * 保存 Session 数据 * @return void * @throws */ public static function save(): void { if (!self::$started) { return; } $sessionId = Context::get(self::SESSION_ID_KEY); $sessionData = Context::get(self::SESSION_KEY); if ($sessionId === null || $sessionData === null) { return; } $filePath = self::getSessionFilePath($sessionId); $data = [ 'data' => $sessionData, 'expires_at' => time() + self::$lifetime, 'created_at' => time(), ]; file_put_contents($filePath, json_encode($data, JSON_UNESCAPED_UNICODE)); // 设置 Cookie self::setSessionCookie($sessionId); } /** * 设置 Session Cookie * @param string $sessionId * @return void * @throws */ private static function setSessionCookie(string $sessionId): void { $response = \response(); $expires = time() + self::$lifetime; // 获取现有的 Cookie 参数 $cookieParams = $response->getCookieParams(); // 添加 Session Cookie // Swoole setCookie 参数: name, value, expires, path, domain, secure, httponly, samesite $cookieParams[] = [ self::SESSION_NAME, // name $sessionId, // value $expires, // expires '/', // path '', // domain false, // secure true, // httponly 'Lax' // samesite ]; $response->withCookieParams($cookieParams); } /** * 获取 Session 文件路径 * @param string $sessionId * @return string */ private static function getSessionFilePath(string $sessionId): string { if (self::$savePath === null) { self::init(); } return self::$savePath . '/' . $sessionId . '.json'; } /** * 销毁 Session 文件 * @param string $sessionId * @return void */ private static function destroySession(string $sessionId): void { $filePath = self::getSessionFilePath($sessionId); if (file_exists($filePath)) { unlink($filePath); } } /** * 存储 Session 数据 * @param string|array $key 键名或键值对数组 * @param mixed $value 值(当 $key 为字符串时) * @return void * @throws */ public static function put(string|array $key, mixed $value = null): void { if (!self::$started) { self::start(); } $sessionData = Context::get(self::SESSION_KEY, []); // 支持批量设置 if (is_array($key)) { $sessionData = array_merge($sessionData, $key); } else { $sessionData[$key] = $value; } Context::set(self::SESSION_KEY, $sessionData); } /** * 获取 Session 数据 * @param string|null $key 键名,为 null 时返回所有数据 * @param mixed $default 默认值 * @return mixed * @throws */ public static function get(?string $key = null, mixed $default = null): mixed { if (!self::$started) { self::start(); } $sessionData = Context::get(self::SESSION_KEY, []); // 如果 key 为 null,返回所有数据 if ($key === null) { return $sessionData; } return $sessionData[$key] ?? $default; } /** * 检查 Session 中是否存在指定键 * @param string $key * @return bool * @throws */ public static function has(string $key): bool { if (!self::$started) { self::start(); } $sessionData = Context::get(self::SESSION_KEY, []); return isset($sessionData[$key]); } /** * 删除 Session 数据 * @param string $key * @return void * @throws */ public static function forget(string $key): void { if (!self::$started) { self::start(); } $sessionData = Context::get(self::SESSION_KEY, []); unset($sessionData[$key]); Context::set(self::SESSION_KEY, $sessionData); } /** * 清空所有 Session 数据 * @return void * @throws */ public static function flush(): void { if (!self::$started) { self::start(); } Context::set(self::SESSION_KEY, []); } /** * 获取所有 Session 数据 * @return array * @throws */ public static function all(): array { if (!self::$started) { self::start(); } return Context::get(self::SESSION_KEY, []); } /** * 销毁 Session * @return void * @throws */ public static function destroy(): void { if (!self::$started) { return; } $sessionId = Context::get(self::SESSION_ID_KEY); if ($sessionId !== null) { self::destroySession($sessionId); } Context::set(self::SESSION_KEY, []); Context::set(self::SESSION_ID_KEY, null); self::$started = false; } /** * 重新生成 Session ID * @return void * @throws */ public static function regenerate(): void { if (!self::$started) { self::start(); } $oldSessionId = Context::get(self::SESSION_ID_KEY); // 生成新的 Session ID $newSessionId = self::generateSessionId(); // 删除旧 Session 文件 if ($oldSessionId !== null) { self::destroySession($oldSessionId); } // 保存新 Session Context::set(self::SESSION_ID_KEY, $newSessionId); self::setSessionCookie($newSessionId); } }