最常见报错是Serialization of 'Closure' is not allowed或RedisException: Cannot serialize closure,本质是phpredis在Cache::put()、Redis::set()或队列dispatch()时强制要求值可PHP原生序列化,而Laravel 6.x默认用phpredis且无自动fallback机制。

phpredis 序列化失败的典型报错长什么样
最常见的是 Serialization of 'Closure' is not allowed、Serialization of 'PDO' is not allowed,或者直接抛出 RedisException: Cannot serialize closure。这类错误不会出现在 Redis 连接阶段,而是在你调用 Cache::put()、Redis::set() 或队列 dispatch() 时触发——本质是 phpredis 扩展在底层尝试序列化 PHP 值失败。
为什么 Laravel 6.x 特别容易踩这个坑
Laravel 6.x 默认使用 phpredis(而非 predis)作为 Redis 客户端,而 phpredis 对序列化行为更严格:它不自动 fallback 到 serialize(),也不像 predis 那样默认支持 JSON 序列化。一旦你往缓存或队列里塞了闭包、资源句柄、未实现 __serialize() 的对象(比如旧版 Eloquent 模型),就会立刻崩。
-
phpredis的set()方法默认要求值必须可被 PHP 原生serialize()处理 - Laravel 6.x 的
Cache门面默认走phpredis的原生序列化,不自动过滤不可序列化字段 - 队列任务中若传了
$request、function() {}或未清理的第三方 SDK 实例,php artisan queue:work启动时就卡住或静默失败
怎么快速定位哪个值导致序列化失败
别靠猜。在写入前加一层检测:
- 对要缓存的变量,先运行
var_dump(is_serializable($value))(PHP 8.2+);低版本可用function is_serializable($var) { return @serialize($var) !== false; } - 如果值是数组或对象,用
array_walk_recursive()遍历每个子项,对每个值做gettype()判断,重点拦截resource、object类型中含Closure、PDO、mysqli等关键词的实例 - 队列任务类里,在
__construct()开头加if (!is_serializable($this)) { throw new \Exception('Job contains unserializable property'); }
绕过问题的实操方案(不改架构前提下)
有三种轻量级解法,按优先级排序:
立即学习“PHP免费学习笔记(深入)”;
- 改用
json序列化:在config/database.php的redis.default配置块里加'options' => ['serializer' => Redis::SERIALIZER_JSON](需 phpredis ≥ 5.3.0) - 缓存前手动净化:把模型转成数组
$user->toArray(),闭包替换成 ID 或字符串标记,资源句柄设为null - 换驱动兜底:临时切回
predis(composer require predis/predis,然后REDIS_CLIENT=predis),它对不可序列化值更宽容,但注意它不支持SERIALIZER_JSON选项
真正难处理的不是报错本身,而是那些没报错却悄悄丢数据的情况——比如队列任务进了 Redis 但无法反序列化,queue:work 就跳过它继续消费下一条,连日志都不留。所以检测逻辑一定要前置,不能只等 failed_jobs 表里出现记录才反应。



















