PHP 8.1 不支持 session.save_handler = mongodb,必须实现 SessionHandlerInterface 并用 session_set_save_handler() 注册;read() 必须返回空字符串而非 null,write() 需更新 expires_at 以启用 TTL 自动清理。

PHP 8.1 默认会话仍存于文件系统,要存到 MongoDB 必须手动接管会话存储逻辑——不能只装扩展、不重写 session_set_save_handler(),否则 session_start() 依然读写 /tmp。
为什么不能直接用 session.save_handler = mongodb
PHP 内置的 session.save_handler 不支持 mongodb 值;它只认 files、redis、memcached 等少数几个。MongoDB 没有被编译进 PHP 核心,所以该配置项设成 mongodb 会被忽略,PHP 回退到默认的 files,且不报错。
必须实现自定义 SessionHandlerInterface
核心是用 MongoDB\Client 操作集合,并让 PHP 调用你的读/写/销毁等方法。关键点:
-
read($id)必须返回字符串(不是数组),且若会话不存在,必须返回空字符串'',不能返回null或抛异常,否则session_start()失败 -
write($id, $session_data)中的$session_data是 PHP 序列化后的原始字符串(类似user|s:4:"john";login_time|i:1726583400;),别试图json_decode它 - 集合建议加 TTL 索引自动清理过期会话:
$collection->createIndex(['expires_at' => 1], ['expireAfterSeconds' => 0]) - 务必在
write()中更新expires_at字段,否则 TTL 不生效
示例片段:
class MongoSessionHandler implements \SessionHandlerInterface
{
private $collection;
private $lifetime;
<pre class="brush:php;toolbar:false;">public function __construct(MongoDB\Collection $collection, int $lifetime = 1440)
{
$this->collection = $collection;
$this->lifetime = $lifetime;
}
public function read($id): string
{
$doc = $this->collection->findOne(['_id' => $id]);
return $doc && isset($doc['data']) ? (string)$doc['data'] : '';
}
public function write($id, $session_data): bool
{
$expires = new MongoDB\BSON\UTCDateTime(strtotime("+{$this->lifetime} seconds") * 1000);
$this->collection->updateOne(
['_id' => $id],
[
'$set' => [
'data' => $session_data,
'expires_at'=> $expires,
],
'$setOnInsert' => ['created_at' => new MongoDB\BSON\UTCDateTime()],
],
['upsert' => true]
);
return true;
}
// ... 其他方法(open/close/destroy/gc)需完整实现}
PHP-FPM 下 session.gc_maxlifetime 可能失效
当使用自定义 handler 时,session.gc_maxlifetime 不再触发自动清理,gc() 方法也不会被自动调用——除非你显式配置 session.gc_probability 和 session.gc_divisor 并确保请求中随机触发。更可靠的做法是:
- 完全弃用
gc(),依赖 MongoDB 的 TTL 索引自动删除过期文档 - 避免在
gc()中执行deleteMany(),高并发下易锁表或超时 - 确认
php.ini中session.use_strict_mode = 1,防止会话固定攻击
真正麻烦的不是连接 MongoDB,而是把 PHP 会话协议那套生命周期语义(尤其是空读返回 ''、写入即覆盖、TTL 与 GC 分离)准确映射到 BSON 文档上——漏掉任一契约点,都会导致登录态莫名丢失或重复登录。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











