PHP 7.3 中 (array)$obj 会丢失私有/受保护属性且不递归,因强制转换仅浅层映射并保留不可读键名;可靠方案是结合反射、递归和显式控制,优先尊重 JsonSerializable 或 __toArray() 约定,并防范循环引用与资源类型。

PHP 7.3 中直接用 (array)$obj 转换对象,确实常出现“转不全”的情况——私有/受保护属性丢失、嵌套对象没展开、JSON 不兼容结构残留。这不是 Bug,而是 PHP 类型转换的固有行为。真正可靠的方式是**递归 + 反射(Reflection) + 显式控制**。
为什么强制类型转换会丢数据?
PHP 的 (array) 强转仅做浅层映射:
- 公有属性正常转为键值对
- 受保护属性变成
"\0*\0key" => value格式(不可读且 JSON 不支持) - 私有属性变成
"\0ClassName\0key" => value,更难处理 - 完全不递归——内部对象、数组里的对象一律保持原样
用 Reflection 递归提取所有可访问属性
反射能绕过访问控制,安全读取私有/受保护属性,再逐层展开嵌套对象:
function objectToArray($obj) {
if (!is_object($obj) && !is_array($obj)) {
return $obj;
}
if (is_object($obj)) {
$obj = (array) $obj;
}
$result = [];
foreach ($obj as $key => $value) {
// 处理魔术属性名(\0*\0 或 \0Class\0)
if (strpos($key, "\0") === 0) {
$key = preg_replace('/^\0.*?\0/', '', $key);
}
$result[$key] = objectToArray($value);
}
return $result;
}
这段代码自动剥离反射生成的隐藏前缀,保留原始语义键名,同时递归处理每一层。
立即学习“PHP免费学习笔记(深入)”;
更健壮:支持 JsonSerializable 和 __toArray() 约定
实际项目中建议优先尊重对象自身的序列化逻辑:
- 若对象实现了
JsonSerializable,调用jsonSerialize() - 若定义了
__toArray()方法(常见于 Laravel Eloquent 或自定义基类),优先使用它 - 否则才回落到反射方案
这样既兼容框架约定,又避免侵入性操作。
注意循环引用和资源类型
递归转换时容易遇到对象互相引用(如父子关系),导致无限循环或内存溢出:
- 维护一个已处理对象的 ID 缓存(
spl_object_id($obj)) - 遇到重复 ID 时返回占位符(如
"[circular reference]")或跳过 - 显式过滤
resource、closure等不可序列化类型,防止报错
简单场景可省略,但生产环境建议加入基础防护。



















