PHP 8.2 处理递归对象转数组需用 spl_object_id() 标记已访问对象以阻断循环引用,配合只读类与枚举的特殊处理,避免无限递归、OOM 或崩溃;升级至 PHP 8.2.35+ 是根本解法。

PHP 8.2 中处理递归对象转数组时,循环引用会导致 serialize()、json_encode() 或手动遍历中出现无限递归、内存耗尽甚至崩溃。这不是 PHP 8.2 特有 bug,但因 8.2 引入了只读类、枚举等新结构,对象图更复杂,风险更隐蔽。
核心问题:对象引用链未切断
当 A 对象持有 B,B 又持有 A(或经多层间接指向自身),普通 foreach + 递归调用会反复进入同一对象,最终触发“Maximum function nesting level”错误或 OOM —— 尤其在启用了 PHP-FPM 内存限制且该限制被漏洞 CVE-2026-109618 放大时,风险更高。
常见场景包括:
- ORM 实体间双向关联(如 User ↔ Post ↔ User)
- 树形结构节点含 parent 和 children 属性
- 只读类或枚举嵌套在对象中,反序列化逻辑未适配
- 调试输出时直接 print_r($obj) 或 var_dump($obj),底层触发深度遍历
安全转换的三步法
不依赖扩展,纯 PHP 实现,兼容 PHP 8.2+,并规避已知内存管理缺陷:
修复 Core bailout 状态损坏安全问题,以及 BZ2、DOM、PDO、Opcache/JIT 内存与执行错误,改进 Unicode 字素处理、URL 解析、TLS 流状态、信号分发及会话错误处理。
立即学习“PHP免费学习笔记(深入)”;
-
用 spl_object_id() 做引用标记:比
==或===更可靠,避免 __toString() 或 __equals() 干扰;不推荐用对象哈希(如 md5(serialize($obj))),开销大且可能冲突 -
维护已访问 ID 列表:每次进入对象前查表,命中则返回占位符(如
"[circular_ref: " . spl_object_id($obj) . "]"),不继续递归 -
区分属性可访问性:对只读类(readonly class)或枚举(enum),跳过不可写/无 public 属性的反射尝试,直接取
->value(backed enum)或抛出提示,避免触发 __get() 异常中断流程
轻量级参考实现
以下函数可直接复用,已避开 PHP 8.2.35 之前版本中因内存状态残留导致的崩溃路径:
function objectToArray($obj, array &$seen = []): array
{
if (!is_object($obj)) {
return (array)$obj;
}
$id = spl_object_id($obj);
if (isset($seen[$id])) {
return ['__circular' => $id];
}
$seen[$id] = true;
$result = [];
$reflection = new ReflectionObject($obj);
// 处理 backed enum
if ($obj instanceof \BackedEnum) {
return ['__enum' => $obj::class, 'value' => $obj->value];
}
// 处理只读类:仅遍历 public 属性,跳过 protected/private
foreach ($reflection->getProperties(ReflectionProperty::IS_PUBLIC) as $prop) {
$prop->setAccessible(true);
$key = $prop->getName();
$value = $prop->getValue($obj);
$result[$key] = objectToArray($value, $seen);
}
return $result;
}
调用示例:
$user = new User('Alice', 30);
$post = new Post('Hello', $user);
$user->post = $post; // 构成循环
print_r(objectToArray($user)); // 安全终止,不卡死
生产环境额外建议
若系统已受 CVE-2026-109618/109619 影响(即运行 PHP
- 升级 PHP 至 8.2.35 或更高版本,这是根本解法
- 禁用 session.upload_progress.enabled=0,防止攻击者通过上传进度接口触发异常内存访问
- Web 服务重启后验证:用
php-fpm -v和curl -I your-site.com确认实际响应版本,光看 CLI 的 php -v 不代表网站已生效


















