PHP 7.4 中健壮的递归对象转数组函数使用 ReflectionObject 访问私有/受保护属性,通过 spl_object_id 检测循环引用,优先调用 JsonSerializable 或 __toArray(),跳过资源与闭包,并静默降级异常值。

在 PHP 7.4 中编写递归对象转数组函数,核心是处理对象属性的遍历、类型判断(尤其是对象、数组、资源、不可序列化值)、循环引用防护,以及兼容 PHP 7.4 的特性(如类型声明、null 合并操作符)。下面是一个健壮、实用、可直接使用的实现。
支持私有/受保护属性与循环引用检测
PHP 默认无法直接访问私有或受保护属性,需借助 ReflectionObject。同时,若对象间存在相互引用(如 A→B→A),不加防护会导致无限递归和内存溢出。解决方法是维护一个已遍历对象的引用表(用 spl_object_id 作键)。
- 使用
spl_object_id($obj)唯一标识每个对象(PHP 7.4+ 支持,比spl_object_hash更轻量且稳定) - 跳过资源(resource)、闭包(Closure)、未实现
__toString的对象等不可转换类型 - 对实现了
JsonSerializable或__toArray()方法的对象优先调用其自定义逻辑
基础递归转换函数(带类型安全与错误降级)
以下函数支持 stdClass、自定义类、嵌套对象、数组混合结构,并对异常情况静默降级(如返回 null 或字符串提示),避免中断整个转换流程:
function objectToArray($obj, array &$seen = []): array
{
// 非对象直接返回(含 null、标量、数组)
if (!is_object($obj)) {
return is_array($obj) ? array_map(fn($v) => objectToArray($v, $seen), $obj) : $obj;
}
$objId = spl_object_id($obj);
if (isset($seen[$objId])) {
return ['__circular_ref' => true, '__id' => $objId];
}
$seen[$objId] = true;
// 优先支持 JsonSerializable 和自定义 __toArray()
if ($obj instanceof \JsonSerializable) {
$data = $obj->jsonSerialize();
return is_array($data) ? array_map(fn($v) => objectToArray($v, $seen), $data) : $data;
}
if (method_exists($obj, '__toArray') && is_callable([$obj, '__toArray'])) {
$data = $obj->__toArray();
return is_array($data) ? array_map(fn($v) => objectToArray($v, $seen), $data) : $data;
}
// 反射获取所有可访问属性(含 private/protected)
$ref = new \ReflectionObject($obj);
$props = $ref->getProperties();
$result = [];
foreach ($props as $prop) {
$prop->setAccessible(true);
$key = $prop->getName();
$value = $prop->getValue($obj);
// 跳过资源、闭包等不可序列化类型
if (is_resource($value) || $value instanceof \Closure) {
$result[$key] = '__unserializable';
continue;
}
// 递归处理
$result[$key] = objectToArray($value, $seen);
}
return $result;
}
使用示例与注意事项
该函数可直接用于 Laravel 模型、DTO 类、API 返回对象等场景:
- 调用时无需传入
$seen参数(默认空数组,由函数内部维护) - 遇到循环引用时返回
['__circular_ref' => true],便于前端识别或日志记录 - 若需忽略某些属性(如 Laravel 的
$casts、$dates),可在反射前加白名单/黑名单逻辑 - 如需保留原始对象类名,可添加
'__class' => get_class($obj)到结果中
简化版(无反射,仅 public 属性)
若只处理 stdClass 或明确只有 public 属性的对象,可用更轻量版本:
function objectToArraySimple($obj): array
{
if (!is_object($obj) && !is_array($obj)) {
return $obj;
}
if (is_object($obj)) {
$obj = (array) $obj;
}
return array_map('objectToArraySimple', $obj);
}
⚠️ 注意:(array)$obj 仅导出 public 属性,private/protected 属性会变成 "\0Classname\0prop" 形式键名,不易读且不通用,生产环境推荐使用反射版。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











