将一个PHP对象转换为关联数组

990

我正在将一个API集成到我的网站上,它使用存储在对象中的数据,而我的代码是使用数组编写的。

我想要一个快速且简单的函数来将对象转换为数组。

34个回答

1773
只需将其归类
$array = (array) $yourObject;

Arrays中:

如果一个对象被转换为数组,结果将是一个数组,其元素是对象的属性。键是成员变量名,有一些值得注意的例外:整数属性是不可访问的;私有变量在变量名前面加上类名;受保护的变量在变量名前面加上'*'。这些前置值两边都有空字节。

示例:简单对象

$object = new StdClass;
$object->foo = 1;
$object->bar = 2;

var_dump( (array) $object );

输出:

array(2) {
  'foo' => int(1)
  'bar' => int(2)
}

例子:复杂对象

class Foo
{
    private $foo;
    protected $bar;
    public $baz;

    public function __construct()
    {
        $this->foo = 1;
        $this->bar = 2;
        $this->baz = new StdClass;
    }
}

var_dump( (array) new Foo );

输出(为了清晰起见编辑了 \0):

array(3) {
  '\0Foo\0foo' => int(1)
  '\0*\0bar' => int(2)
  'baz' => class stdClass#2 (0) {}
}

使用var_export而不是var_dump输出:

array (
  '' . "\0" . 'Foo' . "\0" . 'foo' => 1,
  '' . "\0" . '*' . "\0" . 'bar' => 2,
  'baz' =>
  stdClass::__set_state(array(
  )),
)

要了解更多关于 PHP 内置类 stdclass 的信息。您可以 阅读这份文档

以这种方式进行类型转换不会对对象图进行深度转换,并且您需要应用空字节(如手册中所述)才能访问任何非公共属性。因此,当转换 StdClass 对象或仅具有公共属性的对象时,此方法效果最佳。对于快速而简单(您所要求的),它是可以接受的。

还可以查看这篇深入的博客文章:


3
请考虑使用ArrayAccess接口,结合这个解决方案。http://php.net/manual/en/class.arrayaccess.php - alttag
4
如果你使用整数作为键,它们将被转换为字符串,这可能会导致大问题。例如,[1 => "one"] 会变成 ["1" => "one"] - Oleg
3
使用(array)(object)进行类型转换在PHP 4.3及以后的所有版本中都可靠并且行为相同。请参考https://3v4l.org/X6lhm。如果出现语法错误,则说明您做错了什么。 - Gordon
2
@Howie请查看Changelog section for empty。在5.5之前,您不能使用empty表达式。这与类型转换完全无关 ;) - Gordon
2
注意!!!整数属性变得无法访问。虽然可以通过print_r等方式看到它们。 - Vyshnia
显示剩余17条评论

527

通过依靠JSON编码/解码函数的行为,您可以快速将深度嵌套的对象转换为关联数组:

$array = json_decode(json_encode($nested_object), true);

25
如果你想要完整深度的递归转换,那么这是最佳解决方案(当然不介意性能较差)。 - Julian Habekost
2
顺便提一下,这在 PHP 5.5 上似乎不再起作用了,你将会再次得到一个对象数组。 - Christian Maioli M.
17
恭敬地说,我认为它仍然有效...不要忘记将第二个参数设置为true。 - Kirk B
3
第二个参数解决了问题,可在 PHP 5.6.25 中使用。谢谢! - Ju Oliveira
3
你可以通过在类中实现JsonSerializable接口来控制要导出什么和如何导出:http://php.net/manual/en/class.jsonserializable.php。 - Juergen
显示剩余7条评论

101
从第一个谷歌搜索结果中找到的"PHP对象转换为关联数组",我们有以下内容:
/**
 * @param array|object $data
 * @return array
 */
function object_to_array($data)
{
    $result = [];
    foreach ($data as $key => $value)
    {
        $result[$key] = (is_array($value) || is_object($value)) ? object_to_array($value) : $value;
    }
    return $result;
}

原始来源是from codesnippets.joyent.com,根据@SpYk3HH评论进行了更改;另请参阅Object Iteration
将其与json_decode & json_encode的解决方案进行比较,这个似乎更快。这是一个随机基准测试(使用simple time measuring):
$obj = (object) [
    'name'    =>'Mike',
    'surname' =>'Jovanson',
    'age'     =>'45',
    'time'    =>1234567890,
    'country' =>'Germany',
];

##### 100 000 cycles ######
* json_decode(json_encode($var))   : 4.15 sec
* object_to_array($var)            : 0.93 sec

20
就我个人而言,我不喜欢为每个值都调用函数的想法。我有一个类似的版本,但只用了3行代码:function objectToArray($o) { $a = array(); foreach ($o as $k => $v) $a[$k] = (is_array($v) || is_object($v)) ? objectToArray($v): $v; return $a; } 这个函数只对非对象和非数组进行转换,并且只在必要时才回到方法中,避免了重复调用。 - SpYk3HH
22
@SpYk3HH:你自己写答案吗? - DanMan
3
“php object to assoc array”的第一个搜索结果是https://dev59.com/MG855IYBdhLWcg3wbzxl。 - Chris
3
@Ryan json编码和解码无法处理浮点数中的NaN和INFINITE值,可能还有其他问题我暂时想不到,但对于许多情况来说,这可能是更好的选择。至于优化,它需要上下文 - 让我插入一篇我写的关于这个主题的文章 https://www.evidentlycube.com/blog/game-optimization/when-to-optimize。简而言之,不要优化那些在整个应用程序的上下文中所占运行时间很小的东西,因为收益在整个应用程序的上下文中是毫无意义的。 - Maurycy
@SpYk3HH,你的方法已经融入到答案中了。 - T.Todua
显示剩余3条评论

73
如果您的对象属性是公共的,您可以这样做:
$array =  (array) $object;

如果它们是私有的或受保护的,它们在数组中将具有奇怪的键名称。因此,在这种情况下,您需要使用以下函数:

function dismount($object) {
    $reflectionClass = new ReflectionClass(get_class($object));
    $array = array();
    foreach ($reflectionClass->getProperties() as $property) {
        $property->setAccessible(true);
        $array[$property->getName()] = $property->getValue($object);
        $property->setAccessible(false);
    }
    return $array;
}

如果您的属性受到保护,setAccessible(false)会将其更改回受保护的可见性吗?还是会将其变为私有的? - Nick Mitchell
我找到的唯一解决方案是可以使用受保护属性。谢谢。 - dav
4
私有和受保护变量的最佳解决方案! - HIRA THAKUR
这里的代码行 $property->setAccessible(false); 将会在每个属性上执行 - 即使它是公共的... - Francois Bourgeois
我想另一种方法是将其转换为数组,然后剥离私有属性的文本前缀。这样做会更加安全,因为你的方法可能会将公共属性设置为私有。 - Stephen R
不递归,一个带有几个关联对象级别的对象将只转换顶层对象而不是关联对象。 - Pablo Pazos

16

get_object_vars($obj)怎么样?如果您只想访问对象的公共属性,它似乎非常有用。

请参见get_object_vars


16
class Test{
    const A = 1;
    public $b = 'two';
    private $c = test::A;

    public function __toArray(){
        return call_user_func('get_object_vars', $this);
    }
}

$my_test = new Test();
var_dump((array)$my_test);
var_dump($my_test->__toArray());

输出

array(2) {
    ["b"]=>
    string(3) "two"
    ["Testc"]=>
    int(1)
}
array(1) {
    ["b"]=>
    string(3) "two"
}

1
这个解决方案的优缺点是什么?如果一个类被声明为 class Test { const A = 1; public $parent = new Test(); } 呢? - Matteo Gaggiano

14

以下是一些代码:

function object_to_array($data) {
    if ((! is_array($data)) and (! is_object($data)))
        return 'xxx'; // $data;

    $result = array();

    $data = (array) $data;
    foreach ($data as $key => $value) {
        if (is_object($value))
            $value = (array) $value;
        if (is_array($value))
            $result[$key] = object_to_array($value);
        else
            $result[$key] = $value;
    }
    return $result;
}

这对我来说效果最好(但我需要删除“xxx”并返回$data)。 - ESP32

13
将您的对象强制转换为数组。
$arr =  (array) $Obj;

它将解决你的问题。


7
如果你有私有或受保护的属性,那么不会发生这种情况。 - forsberg

12

这里所有其他发布的答案只适用于公共属性。下面是一种使用反射和getter方法可与JavaBeans类似的对象一起使用的解决方案:

function entity2array($entity, $recursionDepth = 2) {
    $result = array();
    $class = new ReflectionClass(get_class($entity));
    foreach ($class->getMethods(ReflectionMethod::IS_PUBLIC) as $method) {
        $methodName = $method->name;
        if (strpos($methodName, "get") === 0 && strlen($methodName) > 3) {
            $propertyName = lcfirst(substr($methodName, 3));
            $value = $method->invoke($entity);

            if (is_object($value)) {
                if ($recursionDepth > 0) {
                    $result[$propertyName] = $this->entity2array($value, $recursionDepth - 1);
                }
                else {
                    $result[$propertyName] = "***";  // Stop recursion
                }
            }
            else {
                $result[$propertyName] = $value;
            }
        }
    }
    return $result;
}

是的,但是...如果您将对象/数组用作变量,这就是所有内容的导向,为什么您需要除“public”属性之外的任何内容呢? - SpYk3HH
@SpYk3HH:我并没有提出这个问题。我甚至不知道为什么有人一开始会更喜欢数组而不是对象。 - Francois Bourgeois
嗯,我经常喜欢将查询结果转换为数组,以便提供一个统一的“列表”进行循环遍历,因为大多数应用程序中必须“循环”的其他事物都倾向于是数组。这样可以轻松编写“通用循环方法”。通常,如果我正在使用对象,则不会循环遍历其属性,而是将其用作对象并根据需要使用这些属性。 - SpYk3HH
2
为什么有人会更喜欢数组而不是对象?我想编写一个方法,通过foreach循环遍历对象属性的值,将它们写入Excel文件。我希望这个方法独立于对象,以便我可以在不同的数组中使用它。因此,我的对象需要一个_toArray()方法。 - Calamity Jane

10

要将对象转换为数组,只需将其显式转换:

$name_of_array = (array) $name_of_object;

这应该会得到赞同。最佳答案。 - Grumpy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接