将PHP对象转换为关联数组

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

我想要一个快速脏函数将对象转换为数组。

1691485 次浏览

从第一个谷歌点击“PHP对象到assoc数组”我们有这个:

function object_to_array($data){if (is_array($data) || is_object($data)){$result = [];foreach ($data as $key => $value){$result[$key] = (is_array($value) || is_object($value)) ? object_to_array($value) : $value;}return $result;}return $data;}

来源是在codesnippets.joyent.com


将其与json_decode & json_encode的解决方案进行比较,这个似乎更快。这是一个随机基准测试(使用简单时间测量):

$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

只是把它打字

$array = (array) $yourObject;

数组

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

示例:简单对象

$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()),)

这种类型转换不会对对象图进行深度转换,你需要应用空字节(如手动引号中所述)来访问任何非公共属性。因此,当转换StdClass对象或仅具有公共属性的对象时,这最有效。对于快速和肮脏(你要求的)来说,没关系。

也看看这篇深入的博客文章:

类型将对象转换为数组。

$arr =  (array) $Obj;

它会解决你的问题。

以下是一些代码:

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;}

当您从数据库中获取数据作为对象时,您可能希望这样做:

// Suppose 'result' is the end product from some query $query
$result = $mysqli->query($query);$result = db_result_to_array($result);
function db_result_to_array($result){$res_array = array();
for ($count=0; $row = $result->fetch_assoc(); $count++)$res_array[$count] = $row;
return $res_array;}

此处发布的所有其他答案仅适用于公共属性。这是一个使用反射和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;}

如果你的对象属性是公共的,你可以这样做:

$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;}

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

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

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

get_object_vars

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"}

转换和删除烦人的星星:

$array = (array) $object;foreach($array as $key => $val){$new_array[str_replace('*_', '', $key)] = $val;}

也许,它会比使用反射更便宜。

这是我的递归PHP函数,用于将PHP对象转换为关联数组:

// ---------------------------------------------------------// ----- object_to_array_recursive --- function (PHP) ------// ---------------------------------------------------------// --- arg1: -- $object  =  PHP Object         - required --// --- arg2: -- $assoc   =  TRUE or FALSE      - optional --// --- arg3: -- $empty   =  '' (Empty String)  - optional --// ---------------------------------------------------------// ----- Return: Array from Object --- (associative) -------// ---------------------------------------------------------
function object_to_array_recursive($object, $assoc=TRUE, $empty=''){$res_arr = array();
if (!empty($object)) {
$arrObj = is_object($object) ? get_object_vars($object) : $object;
$i=0;foreach ($arrObj as $key => $val) {$akey = ($assoc !== FALSE) ? $key : $i;if (is_array($val) || is_object($val)) {$res_arr[$akey] = (empty($val)) ? $empty : object_to_array_recursive($val);}else {$res_arr[$akey] = (empty($val)) ? $empty : (string)$val;}$i++;}}return $res_arr;}
// ---------------------------------------------------------// ---------------------------------------------------------

使用示例:

// ---- Return associative array from object, ... use:$new_arr1 = object_to_array_recursive($my_object);// -- or --// $new_arr1 = object_to_array_recursive($my_object, TRUE);// -- or --// $new_arr1 = object_to_array_recursive($my_object, 1);

// ---- Return numeric array from object, ... use:$new_arr2 = object_to_array_recursive($my_object, FALSE);

将stdClass转换为数组的自定义函数:

function objectToArray($d) {if (is_object($d)) {// Gets the properties of the given object// with get_object_vars function$d = get_object_vars($d);}
if (is_array($d)) {/** Return array converted to object* Using __FUNCTION__ (Magic constant)* for recursive call*/return array_map(__FUNCTION__, $d);} else {// Return arrayreturn $d;}}

另一个将Array转换为stdClass的自定义函数:

function arrayToObject($d) {if (is_array($d)) {/** Return array converted to object* Using __FUNCTION__ (Magic constant)* for recursive call*/return (object) array_map(__FUNCTION__, $d);} else {// Return objectreturn $d;}}

使用示例:

// Create new stdClass Object$init = new stdClass;
// Add some test data$init->foo = "Test data";$init->bar = new stdClass;$init->bar->baaz = "Testing";$init->bar->fooz = new stdClass;$init->bar->fooz->baz = "Testing again";$init->foox = "Just test";
// Convert array to object and then object back to array$array = objectToArray($init);$object = arrayToObject($array);
// Print objects and arrayprint_r($init);echo "\n";print_r($array);echo "\n";print_r($object);
$Menu = new Admin_Model_DbTable_Menu();$row = $Menu->fetchRow($Menu->select()->where('id = ?', $id));$Addmenu = new Admin_Form_Addmenu();$Addmenu->populate($row->toArray());

用途:

function readObject($object) {$name = get_class ($object);$name = str_replace('\\', "\\\\", $name); // Outcomment this line, if you don't use// class namespaces approach in your project$raw = (array)$object;
$attributes = array();foreach ($raw as $attr => $val) {$attributes[preg_replace('('.$name.'|\*|)', '', $attr)] = $val;}return $attributes;}

它返回一个没有特殊字符和类名的数组。

这里我做了一个Array()对象类型方法,它也适用于递归对象,比如当$objectA包含$objectB时,它再次指向$objectA

此外,我使用RefltionClass将输出限制为公共属性。如果您不需要它,请摆脱它。

    /*** Converts given object to array, recursively.* Just outputs public properties.** @param object|array $object* @return array|string*/protected function objectToArray($object) {if (in_array($object, $this->usedObjects, TRUE)) {return '**recursive**';}if (is_array($object) || is_object($object)) {if (is_object($object)) {$this->usedObjects[] = $object;}$result = array();$reflectorClass = new \ReflectionClass(get_class($this));foreach ($object as $key => $value) {if ($reflectorClass->hasProperty($key) && $reflectorClass->getProperty($key)->isPublic()) {$result[$key] = $this->objectToArray($value);}}return $result;}return $object;}

为了识别已经使用的对象,我在这个(抽象)类中使用了一个名为$this->usedObjects的受保护属性。如果找到递归嵌套对象,它将被字符串**recursive**替换。否则,由于无限循环,它将失败。

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

$name_of_array = (array) $name_of_object;

对“良知”守则的一些影响

/*** mixed Obj2Array(mixed Obj)***************************************/static public function Obj2Array($_Obj) {if (is_object($_Obj))$_Obj = get_object_vars($_Obj);return(is_array($_Obj) ? array_map(__METHOD__, $_Obj) : $_Obj);} // BW_Conv::Obj2Array

请注意,如果函数是类的成员(如上所述),则必须将__FUNCTION__更改为__METHOD__

首先,如果您需要一个对象的数组,您可能应该首先将数据构成为数组。考虑一下。

不要使用foreach语句或JSON转换。如果您计划这样做,您再次使用数据结构,而不是对象。

如果你真的需要它,请使用面向对象的方法来获得干净且可维护的代码。例如:

对象作为数组

class PersonArray implements \ArrayAccess, \IteratorAggregate{public function __construct(Person $person) {$this->person = $person;}// ...}

如果您需要所有属性,请使用传输对象:

class PersonTransferObject{private $person;
public function __construct(Person $person) {$this->person = $person;}
public function toArray() {return [// 'name' => $this->person->getName();];}
}

由于很多人发现这个问题是因为在动态访问对象属性时遇到问题,我只想指出您可以在PHP中做到这一点:$valueRow->{"valueName"}

上下文中(删除了超文本标记语言输出的易读性):

$valueRows = json_decode("{...}"); // Rows of unordered values decoded from a JSON object
foreach ($valueRows as $valueRow) {
foreach ($references as $reference) {
if (isset($valueRow->{$reference->valueName})) {$tableHtml .= $valueRow->{$reference->valueName};}else {$tableHtml .= " ";}}}

您可以轻松使用此函数来获取结果:

function objetToArray($adminBar){$reflector = new ReflectionObject($adminBar);$nodes = $reflector->getProperties();$out = [];foreach ($nodes as $node) {$nod = $reflector->getProperty($node->getName());$nod->setAccessible(true);$out[$node->getName()] = $nod->getValue($adminBar);}return $out;}

使用php5或更高版本。

这个答案只是本文不同答案的结合,但它是将具有公共或私有属性的PHP对象与简单值或数组转换为关联数组的解决方案…

function object_to_array($obj){if (is_object($obj))$obj = (array)$this->dismount($obj);if (is_array($obj)) {$new = array();foreach ($obj as $key => $val) {$new[$key] = $this->object_to_array($val);}}else$new = $obj;return $new;}
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;}

@SpYk3HH的简短解决方案

function objectToArray($o){$a = array();foreach ($o as $k => $v)$a[$k] = (is_array($v) || is_object($v)) ? objectToArray($v): $v;
return $a;}

也可以使用Symfony序列化器组件

use Symfony\Component\Serializer\Encoder\JsonEncoder;use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;use Symfony\Component\Serializer\Serializer;
$serializer = new Serializer([new ObjectNormalizer()], [new JsonEncoder()]);$array = json_decode($serializer->serialize($object, 'json'), true);

您还可以在PHP中创建一个函数来转换对象数组:

function object_to_array($object) {return (array) $object;}

通过使用类型转换,您可以解决问题。只需将以下行添加到您的返回对象:

$arrObj = array(yourReturnedObject);

您还可以使用以下命令向其添加新的键和值对:

$arrObj['key'] = value;

这是我的建议,如果你在对象中有对象,甚至有私有成员:

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

对于您的情况,如果您使用“装饰器”或“日期模型转换”模式,这是正确/美丽的。例如:

你的模特

class Car {/** @var int */private $color;
/** @var string */private $model;
/** @var string */private $type;
/*** @return int*/public function getColor(): int{return $this->color;}
/*** @param int $color* @return Car*/public function setColor(int $color): Car{$this->color = $color;return $this;}
/*** @return string*/public function getModel(): string{return $this->model;}
/*** @param string $model* @return Car*/public function setModel(string $model): Car{$this->model = $model;
return $this;}
/*** @return string*/public function getType(): string{return $this->type;}
/*** @param string $type* @return Car*/public function setType(string $type): Car{$this->type = $type;
return $this;}}

装饰器

class CarArrayDecorator{/** @var Car */private $car;
/*** CarArrayDecorator constructor.* @param Car $car*/public function __construct(Car $car){$this->car = $car;}
/*** @return array*/public function getArray(): array{return ['color' => $this->car->getColor(),'type' => $this->car->getType(),'model' => $this->car->getModel(),];}}

用法

$car = new Car();$car->setType('type#');$car->setModel('model#1');$car->setColor(255);
$carDecorator = new CarArrayDecorator($car);$carResponseData = $carDecorator->getArray();

所以它会更漂亮,更正确的代码。

我认为使用特征来存储对象到数组的转换逻辑是一个好主意。一个简单的例子:

trait ArrayAwareTrait{/*** Return list of Entity's parameters* @return array*/public function toArray(){$props = array_flip($this->getPropertiesList());return array_map(function ($item) {if ($item instanceof \DateTime) {return $item->format(DATE_ATOM);}return $item;},array_filter(get_object_vars($this), function ($key) use ($props) {return array_key_exists($key, $props);}, ARRAY_FILTER_USE_KEY));}

/*** @return array*/protected function getPropertiesList(){if (method_exists($this, '__sleep')) {return $this->__sleep();}if (defined('static::PROPERTIES')) {return static::PROPERTIES;}return [];}}
class OrderResponse{use ArrayAwareTrait;
const PROP_ORDER_ID = 'orderId';const PROP_TITLE = 'title';const PROP_QUANTITY = 'quantity';const PROP_BUYER_USERNAME = 'buyerUsername';const PROP_COST_VALUE = 'costValue';const PROP_ADDRESS = 'address';
private $orderId;private $title;private $quantity;private $buyerUsername;private $costValue;private $address;
/*** @param $orderId* @param $title* @param $quantity* @param $buyerUsername* @param $costValue* @param $address*/public function __construct($orderId,$title,$quantity,$buyerUsername,$costValue,$address) {$this->orderId = $orderId;$this->title = $title;$this->quantity = $quantity;$this->buyerUsername = $buyerUsername;$this->costValue = $costValue;$this->address = $address;}
/*** @inheritDoc*/public function __sleep(){return [static::PROP_ORDER_ID,static::PROP_TITLE,static::PROP_QUANTITY,static::PROP_BUYER_USERNAME,static::PROP_COST_VALUE,static::PROP_ADDRESS,];}
/*** @return mixed*/public function getOrderId(){return $this->orderId;}
/*** @return mixed*/public function getTitle(){return $this->title;}
/*** @return mixed*/public function getQuantity(){return $this->quantity;}
/*** @return mixed*/public function getBuyerUsername(){return $this->buyerUsername;}
/*** @return mixed*/public function getCostValue(){return $this->costValue;}
/*** @return string*/public function getAddress(){return $this->address;}}
$orderResponse = new OrderResponse(...);var_dump($orderResponse->toArray());

不是一个新的解决方案,但包括toArray方法转换(如果可用)

function objectToArray($r){if (is_object($r)) {if (method_exists($r, 'toArray')) {return $r->toArray(); // returns result directly} else {$r = get_object_vars($r);}}
if (is_array($r)) {$r = array_map(__FUNCTION__, $r); // recursive function call}
return $r;}

我使用这个(需要带有正确键的递归解决方案):

    /*** This method returns the array corresponding to an object, including non public members.** If the deep flag is true, is will operate recursively, otherwise (if false) just at the first level.** @param object $obj* @param bool $deep = true* @return array* @throws \Exception*/public static function objectToArray(object $obj, bool $deep = true){$reflectionClass = new \ReflectionClass(get_class($obj));$array = [];foreach ($reflectionClass->getProperties() as $property) {$property->setAccessible(true);$val = $property->getValue($obj);if (true === $deep && is_object($val)) {$val = self::objectToArray($val);}$array[$property->getName()] = $val;$property->setAccessible(false);}return $array;}

使用示例,以下代码:

class AA{public $bb = null;protected $one = 11;
}
class BB{protected $two = 22;}

$a = new AA();$b = new BB();$a->bb = $b;
var_dump($a)

将打印这个:

array(2) {["bb"] => array(1) {["two"] => int(22)}["one"] => int(11)}

为了单元测试的目的,我建议使用json_decode(json_encode($object), true)代替。因为如果您尝试对如下所示的复杂对象使用(array) $obj,单元测试将失败。考虑以下测试用例:

输入图片描述

我在Laravel集合中测试了它,这工作得很好。

function objectToArray($data){if (is_array($data) || is_object($data)){$result = [];foreach ($data as $key => $value){$result[$key] = (is_array($data) || is_object($data)) ? objectToArray($value) : $value;}return $result;}return $data;}