修正看看
This commit is contained in:
@@ -17,6 +17,7 @@ class App
|
||||
// 1. 时区 & 会话
|
||||
date_default_timezone_set(self::config('app.timezone', 'Asia/Shanghai'));
|
||||
if (session_status() !== PHP_SESSION_ACTIVE) {
|
||||
self::ensureSessionPath();
|
||||
session_start([
|
||||
'cookie_httponly' => true,
|
||||
'cookie_samesite' => 'Lax',
|
||||
@@ -48,6 +49,43 @@ class App
|
||||
self::dispatch(self::parseRoute());
|
||||
}
|
||||
|
||||
/**
|
||||
* 确保 session 存储路径可用。
|
||||
* 服务器 session.save_path 可能指向不存在/不可写目录(如旧域名残留配置、
|
||||
* php_admin_value 锁定等),此时 session_start() 会报 Warning 并失败。
|
||||
* 本方法依次尝试:1) session_save_path() 切换到项目本地目录;2) 若被锁则
|
||||
* 注册自定义文件 session handler 完全绕过服务器配置。
|
||||
*/
|
||||
private static function ensureSessionPath()
|
||||
{
|
||||
$sp = session_save_path();
|
||||
// session.save_path 可能带 N;/path 深度前缀,取实际路径部分判断
|
||||
$spDir = ($pos = strpos($sp, ';')) !== false ? substr($sp, $pos + 1) : $sp;
|
||||
if ($spDir !== '' && is_dir($spDir) && is_writable($spDir)) {
|
||||
return; // 服务器路径正常,无需处理
|
||||
}
|
||||
|
||||
$localSession = BASE_PATH . '/storage/sessions';
|
||||
if (!is_dir($localSession)) {
|
||||
@mkdir($localSession, 0755, true);
|
||||
}
|
||||
if (!is_dir($localSession) || !is_writable($localSession)) {
|
||||
return; // 本地目录也建不了,交给 session_start() 原样报错
|
||||
}
|
||||
|
||||
// 尝试 1:session_save_path() 切换(php_value 级别可生效)
|
||||
session_save_path($localSession);
|
||||
$checkPath = session_save_path();
|
||||
$checkDir = ($pos = strpos($checkPath, ';')) !== false ? substr($checkPath, $pos + 1) : $checkPath;
|
||||
if ($checkDir === $localSession) {
|
||||
return; // 切换成功
|
||||
}
|
||||
|
||||
// 尝试 2:被 php_admin_value 锁定,注册自定义文件 handler 完全绕过
|
||||
$handler = new LocalSessionHandler($localSession);
|
||||
session_set_save_handler($handler, true);
|
||||
}
|
||||
|
||||
/** 解析请求路径为段数组 */
|
||||
public static function parseRoute(): array
|
||||
{
|
||||
@@ -405,3 +443,59 @@ class App
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 本地文件 session handler —— 当服务器 session.save_path 不可用/被锁时,
|
||||
* 将 session 数据存到项目 storage/sessions/ 目录,完全绕过服务器配置。
|
||||
* 兼容 PHP 7.4 ~ 8.x(不声明返回类型,用 #[\ReturnTypeWillChange] 抑制 8.x 弃用提示)。
|
||||
*/
|
||||
class LocalSessionHandler implements \SessionHandlerInterface
|
||||
{
|
||||
private $dir;
|
||||
|
||||
public function __construct(string $dir)
|
||||
{
|
||||
$this->dir = $dir;
|
||||
}
|
||||
|
||||
public function open($savePath, $sessionName)
|
||||
{
|
||||
return is_dir($this->dir) && is_writable($this->dir);
|
||||
}
|
||||
|
||||
public function close()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
#[\ReturnTypeWillChange]
|
||||
public function read($id)
|
||||
{
|
||||
$f = $this->dir . '/sess_' . $id;
|
||||
return is_file($f) ? (string) @file_get_contents($f) : '';
|
||||
}
|
||||
|
||||
public function write($id, $data)
|
||||
{
|
||||
return @file_put_contents($this->dir . '/sess_' . $id, $data) !== false;
|
||||
}
|
||||
|
||||
public function destroy($id)
|
||||
{
|
||||
$f = $this->dir . '/sess_' . $id;
|
||||
return is_file($f) ? @unlink($f) : true;
|
||||
}
|
||||
|
||||
#[\ReturnTypeWillChange]
|
||||
public function gc($maxlifetime)
|
||||
{
|
||||
$n = 0;
|
||||
foreach ((array) @glob($this->dir . '/sess_*') as $f) {
|
||||
if (is_file($f) && filemtime($f) + $maxlifetime < time()) {
|
||||
@unlink($f);
|
||||
$n++;
|
||||
}
|
||||
}
|
||||
return $n;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user