PHP对象怎么转换成数组?几种简便方法一次讲清

来源:Nodejs教程作者:盲改大师头衔:程序员
导读:本期聚焦于盲改大师创作的《PHP对象怎么转换成数组?几种简便方法一次讲清》,敬请观看详情。PHP 对象和数组在底层结构上高度相似,但很多业务场景仍然需要把 stdClass 或自定义类实例转成数组,比如统一接口返回格式、写入缓存、做配置合并。转换时最常用的就是 json_decode(json_encode($object), true),先编码成 JSON 再解码为关联数组。这个方法代码只有一行,能自动处理多层嵌套,但遇到私有属性、资源类型或包含闭包的对象会失效。另一种 get_object_vars 只能读取公开属性,类型强转 (array)$object 同样只转换第一层。若想完整递归并保留私有属性,需要借助反射或写自定义递归函数。本文会对比这些方案的适用边界、性能差异和常见坑点,并给出可复用的递归转换实现。

PHP 中对象和数组的界限并不像其他语言那样严格。stdClass 对象、ORM 返回的模型实例、配置读取器产出的节点对象,经常需要在数组和对象之间切换。转换方式看似简单,但如果只使用类型强转,往往只处理了第一层;如果对象里还嵌套对象或包含私有属性,结果可能和预期相差很大。本文围绕几种实际可用的转换方案展开,对比它们的适用边界和隐藏代价。

PHP对象怎么转换成数组?几种简便方法一次讲清

一、最简单直接的 json_decode 组合

在 PHP 里把对象转成数组,流传最广的一行代码就是 json_decode(json_encode($object), true)。它的原理非常直白:先通过 json_encode 把对象序列化成 JSON 字符串,再利用 json_decode 的第二个参数 true,让解码结果以关联数组的形式返回,而不是默认的 stdClass 对象。整个过程不依赖对象的具体类定义,也不用手动遍历属性。

这种方式的真正优势在于多层嵌套结构可以一次性处理。比如一个对象属性下面还有对象,对象内部又包含数组,只要这些数据都满足 JSON 序列化条件,转换结果会自动递归成多维数组。下面是一个典型示例:

<?php
class Order
{
    public $id = 101;
    public $customer;
    public $items = [];

    public function __construct()
    {
        $this->customer = new stdClass();
        $this->customer->name = 'Tom';
        $this->customer->city = 'Shanghai';
        $this->items[] = ['sku' => 'A001', 'qty' => 2];
    }
}

$order = new Order();
$array = json_decode(json_encode($order), true);

print_r($array);

执行后得到的 $array 完全是一个嵌套关联数组,可以直接用于配置合并、数据库写入或 API 响应。不过这个简便方法也有明显限制。首先 json_encode 只能序列化对象的公开属性,protected 和 private 属性默认会被忽略。其次,如果对象中包含资源类型、闭包、匿名函数或循环引用,JSON 编码会直接失败并返回 false。此外,浮点数在 JSON 编码过程中可能出现精度变化,特殊字符也可能被转义。对于普通数据传输对象来说这些问题不大,但涉及敏感数据时就需要额外处理。

二、get_object_vars 与类型强转

如果不想经过 JSON 这一层,PHP 还提供了更底层的转换方式。get_object_vars($object) 可以返回对象中可见属性的关联数组。注意这里的「可见」取决于调用位置:在类外部只能拿到 public 属性,而在类内部调用时,protected 和 private 属性同样可以被读取出来。类型强转 (array)$object 的行为略有不同,它会把对象的所有属性都塞进数组,但私有属性的键名会带上特殊前缀,包含类名和空字节,不适合直接使用。

通过下面的代码可以直观看到两种方式的差异:

<?php
class User
{
    public $name = 'Alice';
    protected $email = 'alice@ippipp.com';
    private $age = 30;
}

$user = new User();

// 类外调用只返回 public 属性
var_dump(get_object_vars($user));

// 类型强转会带上 private,但键名含特殊前缀
var_dump((array)$user);

从输出结果可以看到,get_object_vars 在类外部返回的数组只包含 name,而 (array)$user 虽然包含了三个属性,但 emailage 的键名已经被污染,形如 Useremail 且夹带空字节。即使把这些非法键名清理掉,另一个问题依然存在:两者都只转换对象本身这一层。如果属性值本身是对象,转换结果里它仍然保持对象类型,只是最外层变成了数组。因此这两种方法更适合结构简单、属性公开的临时对象,不适合深层次数据结构。

三、编写可递归的 object_to_array 函数

要应对嵌套对象,可以自己实现递归转换函数。基本思路是:遇到对象先把它转换成数组,然后遍历数组的每一项,如果值仍然是对象或数组,就递归调用同一个函数继续处理。使用 get_object_vars 作为第一层转换手段时,函数只能拿到公开属性,但这对于大多数数据传输对象已经足够,代码也非常简洁。

<?php
function object_to_array($data)
{
    if (is_object($data)) {
        $data = get_object_vars($data);
    }

    if (is_array($data)) {
        return array_map(__FUNCTION__, $data);
    }

    return $data;
}

$object = new stdClass();
$object->name = 'Demo';
$object->detail = new stdClass();
$object->detail->level = 3;

$array = object_to_array($object);
print_r($array);

这个函数会对数组中的每一项执行 __FUNCTION__ 回调,形成递归。由于 PHP 中 __FUNCTION__ 在函数内部会被解析成当前函数名的字符串,因此 array_map 可以正确调用自身。不过它同样受 get_object_vars 的可见性限制。如果必须读取 protected 和 private 属性,就需要借助反射机制。反射可以绕过访问控制,把所有属性强制设为可访问状态,再逐个读取值并递归转换。

<?php
function object_to_array_deep($obj)
{
    if (is_object($obj)) {
        $reflection = new ReflectionObject($obj);
        $properties = $reflection->getProperties();
        $result = [];

        foreach ($properties as $property) {
            $property->setAccessible(true);
            $value = $property->getValue($obj);
            $result[$property->getName()] = object_to_array_deep($value);
        }

        return $result;
    }

    if (is_array($obj)) {
        return array_map(__FUNCTION__, $obj);
    }

    return $obj;
}

class User
{
    public $name = 'Alice';
    protected $email = 'alice@ippipp.com';
    private $age = 30;
}

$user = new User();
$array = object_to_array_deep($user);
print_r($array);

反射方案的优点是完整、通用,可以拿到全部属性,递归深度也不受 JSON 限制。代价是性能较普通遍历差一些,并且如果属性是静态属性或存在循环引用,还需要额外判断,否则可能陷入无限递归。实际使用时可以给递归函数增加深度参数,超过一定层级就停止转换,避免意外情况。

四、实践中的选择与常见坑点

选择哪种方案,取决于对象从哪里来以及你要用数组做什么。如果对象来源于 json_decode 且只包含公开属性,直接使用 json_decode(json_encode($object), true) 是最省事的,代码简短,多层结构自动转换。如果是自己编写的模型类,更推荐在类内部提供一个 toArray() 方法,手动返回需要暴露的字段。这样既能明确控制输出结构,也能避免反射带来的性能损耗。

只有当你面对一个完全未知的第三方对象,又必须拿到所有私有属性时,才考虑反射递归方案。此时还要注意几个常见问题:第一,JSON 编码默认不会把中文转成 Unicode 转义,但如果你对输出格式有要求,可以传入 JSON_UNESCAPED_UNICODE 保持中文可读;第二,ArrayObject 类型的对象不能直接通过 (array) 转换成普通数组,应当调用 getArrayCopy();第三,空对象经过 json_decode(json_encode($obj), true) 会变成空数组,这是正常行为,但可能影响一些严格类型判断。

对于循环引用对象,任何递归转换都有风险。可以通过维护一个已访问对象标识列表来检测重复对象,或在递归函数中设置最大深度。总的来说,PHP 对象转数组没有唯一定式,简便方案优先,复杂场景再逐步升级。

PHP对象转数组json_decodeget_object_vars修改时间:2026-08-29 02:53:59

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。