Related
I need to deserialize some XML I receive into an object. I use SimpleXML to do this. An example of some of the XML received is this:
<?xml version="1.0" encoding="UTF-8"?>
<coupons type="array">
<coupon>
<coupon_code>baz</coupon_code>
<name>baz</name>
<discount_type>percent</discount_type>
<discount_percent type="integer">20</discount_percent>
<plan_codes type="array">
<plan_code>plus</plan_code>
<plan_code>pro</plan_code>
</plan_codes>
</coupon>
<coupon>
<coupon_code>test</coupon_code>
<name>Test</name>
<discount_type>dollars</discount_type>
<discount_in_cents>
<USD type="integer">2000</USD>
<EUR type="integer">1500</USD>
</discount_in_cents>
<plan_codes type="array">
<plan_code>plus</plan_code>
</plan_codes>
</coupon>
</coupons>
In this piece of XML, there are 3 types of arrays: <coupons> is an array of <coupon>. <coupon> has 2 arrays as well: <plan_codes> (which is a flat array) and <discount_in_cents> which is an associative array.
How does my deserializer work: I create a SimpleXMLElement from the XML and pass it to a function visitElement which takes a SimpleXMLElement and the type, array<Model\Coupon> in this case, indicating it's an array of Model\Coupon objects.
In my visitElement I check if the type is an existing class, if so, I create a new instance of that class and get a configuration for that Model, the configuration tells me which children need to be deserialized to which object variable. visitElement looks like this:
public function visitElement($element, $type)
{
// it is possible the element is not available
if (!$element) {
return null;
} else {
// get the value based on it's mapped type
switch ($type) {
case 'string':
return $this->visitString($element);
break;
// ...
// other cases
// ...
default: // catch other cases
if (class_exists($type)) {
// We mapped to an existing class
return $this->visitModel($type, $element);
} elseif ($this->isArrayType($type, $arrayType, $keepKey)) {
// We mapped to an array<type>
return $this->visitArray($arrayType, $element, $keepKey);
} else {
// A type we can't handle
return null;
}
}
}
}
This way, I have a nice recursive way of deserializing other XML as well, that might have nested objects (For example, the Account XML has an Address object nested within).
My Model\Coupon is configured so that <plan_codes> and <discount_in_cents> are considered array<string> and array<integer> respectively.
This is my visitArray function:
protected function visitArray($type, $element, $keepKey = false)
{
$value = null;
$i = 0;
foreach ($element->children() as $key => $child) {
$k = $keepKey ? $key : $i++;
$value[$k] = $this->visitElement($child, $type);
}
return $value;
}
This all works beautifully. I get an array of 2 Model\Coupon objects, with the second object having a discount_in_cents array that looks like ['USD' => 2000, 'EUR' => 1500], but it does NOT work for the plan codes...
When I var_dump those, I get
array (size=2)
0 => null
1 => null
and
array (size=1)
0 => null
Is there any reason why looping over <plan_codes> would yield different results/children than <coupons> or <discount_in_cents>?
At the moment, I implemented a workaround in visitArray as follows:
protected function visitArray($type, $element, $keepKey = false)
{
$value = null;
$i = 0;
foreach ($element->children() as $key => $child) {
$k = $keepKey ? $key : $i++;
$value[$k] = 'string' === $type ? (string)$child : $this->visitElement($child, $type);
}
return $value;
}
Is it possible to set multiple properties at a time for an object in php?
Instead of doing:
$object->prop1 = $something;
$object->prop2 = $otherthing;
$object->prop3 = $morethings;
do something like:
$object = (object) array(
'prop1' => $something,
'prop2' => $otherthing,
'prop3' => $morethings
);
but without overwriting the object.
Not like the way you want. but this can be done by using a loop.
$map = array(
'prop1' => $something,
'prop2' => $otherthing,
'prop3' => $morethings
);
foreach($map as $k => $v)
$object->$k = $v;
See only 2 extra lines.
You should look at Object Oriented PHP Best Practices :
"since the setter functions return $this you can chain them like so:"
$object->setName('Bob')
->setHairColor('green')
->setAddress('someplace');
This incidentally is known as a fluent interface.
I would recommend you don't do it. Seriously, don't.
Your code is much MUCH cleaner the first way, it's clearer of your intentions, and you aren't obfocusing your code to the extent where sometime in the future someone would look at your code and think "What the hell was the idiot thinking"?
If you insist on doing something which is clearly the wrong way to go, you can always create an array, iterate it and set all the properties in a loop. I won't give you code though. It's evil.
You could write some setters for the object that return the object:
public function setSomething($something)
{
$this->something = $something;
return $this; //this will return the current object
}
You could then do:
$object->setSomething("something")
->setSomethingelse("somethingelse")
->setMoreThings("some more things");
You would need to write a setter for each property as a __set function is not capable of returning a value.
Alternatively, set a single function to accept an array of property => values and set everything?
public function setProperties($array)
{
foreach($array as $property => $value)
{
$this->{$property} = $value;
}
return $this;
}
and pass in the array:
$object->setProperties(array('something' => 'someText', 'somethingElse' => 'more text', 'moreThings'=>'a lot more text'));
I realise this is an old question but for the benefit of others that come across it, I solved this myself recently and wanted to share the result
<?php
//Just some setup
header('Content-Type: text/plain');
$account = (object) array(
'email' => 'foo',
'dob'=>((object)array(
'day'=>1,
'month'=>1,
'year'=>((object)array('century'=>1900,'decade'=>0))
))
);
var_dump($account);
echo "\n\n==============\n\n";
//The functions
function &getObjRef(&$obj,$prop) {
return $obj->{$prop};
}
function updateObjFromArray(&$obj,$array){
foreach ($array as $key=>$value) {
if(!is_array($value))
$obj->{$key} = $value;
else{
$ref = getObjRef($obj,$key);
updateObjFromArray($ref,$value);
}
}
}
//Test
updateObjFromArray($account,array(
'id' => '123',
'email' => 'user#domain.com',
'dob'=>array(
'day'=>19,
'month'=>11,
'year'=>array('century'=>1900,'decade'=>80)
)
));
var_dump($account);
Obviously there are no safeguards built in. The main caveat is that the updateObjFromArray function assumes that for any nested arrays within $array, the corresponding key in $obj already exists and is an object, this must be true or treating it like an object will throw an error.
Hope this helps! :)
I wouldn't actually do this....but for fun I would
$object = (object) ($props + (array) $object);
you end up with an stdClass composed of $objects public properties, so it loses its type.
Method objectThis() to transtypage class array properties or array to stdClass. Using direct transtypage (object) would remove numeric index, but using this method it will keep the numeric index.
public function objectThis($array = null) {
if (!$array) {
foreach ($this as $property_name => $property_values) {
if (is_array($property_values) && !empty($property_values)) {
$this->{$property_name} = $this->objectThis($property_values);
} else if (is_array($property_values) && empty($property_values)) {
$this->{$property_name} = new stdClass();
}
}
} else {
$object = new stdClass();
foreach ($array as $index => $values) {
if (is_array($values) && empty($values)) {
$object->{$index} = new stdClass();
} else if (is_array($values)) {
$object->{$index} = $this->objectThis($values);
} else if (is_object($values)) {
$object->{$index} = $this->objectThis($values);
} else {
$object->{$index} = $values;
}
}
return $object;
}
}
I am developing a web application in PHP,
I need to transfer many objects from server as JSON string, is there any library existing for PHP to convert object to JSON and JSON String to Objec, like Gson library for Java.
This should do the trick!
// convert object => json
$json = json_encode($myObject);
// convert json => object
$obj = json_decode($json);
Here's an example
$foo = new StdClass();
$foo->hello = "world";
$foo->bar = "baz";
$json = json_encode($foo);
echo $json;
//=> {"hello":"world","bar":"baz"}
print_r(json_decode($json));
// stdClass Object
// (
// [hello] => world
// [bar] => baz
// )
If you want the output as an Array instead of an Object, pass true to json_decode
print_r(json_decode($json, true));
// Array
// (
// [hello] => world
// [bar] => baz
// )
More about json_encode()
See also: json_decode()
for more extendability for large scale apps use oop style with encapsulated fields.
Simple way :-
class Fruit implements JsonSerializable {
private $type = 'Apple', $lastEaten = null;
public function __construct() {
$this->lastEaten = new DateTime();
}
public function jsonSerialize() {
return [
'category' => $this->type,
'EatenTime' => $this->lastEaten->format(DateTime::ISO8601)
];
}
}
echo json_encode(new Fruit()); //which outputs:
{"category":"Apple","EatenTime":"2013-01-31T11:17:07-0500"}
Real Gson on PHP :-
http://jmsyst.com/libs/serializer
http://symfony.com/doc/current/components/serializer.html
http://framework.zend.com/manual/current/en/modules/zend.serializer.html
http://fractal.thephpleague.com/ - serialize only
json_decode($json, true);
// the second param being true will return associative array. This one is easy.
PHP8-Code:
class foo{
function __construct(
public $bar,
protected $bat,
private $baz,
){}
function getBar(){return $this->bar;}
function getBat(){return $this->bat;}
function getBaz(){return $this->baz;}
}
//Create Object
$foo = new foo(bar:"bar", bat:"bat", baz:"baz");
//Object => JSON
$fooJSON = json_encode(serialize($foo));
print_r($fooJSON);
// "O:3:\"foo\":3:{s:3:\"bar\";s:3:\"bar\";s:6:\"\u0000*\u0000bat\";s:3:\"bat\";s:8:\"\u0000foo\u0000baz\";s:3:\"baz\";}"
// Important. In order to be able to unserialize() an object, the class of that object needs to be defined.
# More information here: https://www.php.net/manual/en/language.oop5.serialization.php
//JSON => Object
$fooObject = unserialize(json_decode($fooJSON));
print_r($fooObject);
//(
# [bar] => bar
# [bat:protected] => bat
# [baz:foo:private] => baz
# )
//To use some functions or Properties of $fooObject
echo $fooObject->bar;
// bar
echo $fooObject->getBat();
// bat
echo $fooObject->getBaz();
// baz
I made a method to solve this.
My approach is:
1 - Create a abstract class that have a method to convert Objects to Array (including private attr) using Regex.
2 - Convert the returned array to json.
I use this Abstract class as parent of all my domain classes
Class code:
namespace Project\core;
abstract class AbstractEntity {
public function getAvoidedFields() {
return array ();
}
public function toArray() {
$temp = ( array ) $this;
$array = array ();
foreach ( $temp as $k => $v ) {
$k = preg_match ( '/^\x00(?:.*?)\x00(.+)/', $k, $matches ) ? $matches [1] : $k;
if (in_array ( $k, $this->getAvoidedFields () )) {
$array [$k] = "";
} else {
// if it is an object recursive call
if (is_object ( $v ) && $v instanceof AbstractEntity) {
$array [$k] = $v->toArray();
}
// if its an array pass por each item
if (is_array ( $v )) {
foreach ( $v as $key => $value ) {
if (is_object ( $value ) && $value instanceof AbstractEntity) {
$arrayReturn [$key] = $value->toArray();
} else {
$arrayReturn [$key] = $value;
}
}
$array [$k] = $arrayReturn;
}
// if it is not a array and a object return it
if (! is_object ( $v ) && !is_array ( $v )) {
$array [$k] = $v;
}
}
}
return $array;
}
}
I get a strange error using json_decode(). It decode correctly the data (I saw it using print_r), but when I try to access to info inside the array I get:
Fatal error: Cannot use object of type stdClass as array in
C:\Users\Dail\software\abs.php on line 108
I only tried to do: $result['context'] where $result has the data returned by json_decode()
How can I read values inside this array?
Use the second parameter of json_decode to make it return an array:
$result = json_decode($data, true);
The function json_decode() returns an object by default.
You can access the data like this:
var_dump($result->context);
If you have identifiers like from-date (the hyphen would cause a PHP error when using the above method) you have to write:
var_dump($result->{'from-date'});
If you want an array you can do something like this:
$result = json_decode($json, true);
Or cast the object to an array:
$result = (array) json_decode($json);
You must access it using -> since its an object.
Change your code from:
$result['context'];
To:
$result->context;
Use true as the second parameter to json_decode. This will decode the json into an associative array instead of stdObject instances:
$my_array = json_decode($my_json, true);
See the documentation for more details.
Have same problem today, solved like this:
If you call json_decode($somestring) you will get an Object and you need to access like $object->key , but if u call json_decode($somestring, true) you will get an dictionary and can access like $array['key']
It's not an array, it's an object of type stdClass.
You can access it like this:
echo $oResult->context;
More info here: What is stdClass in PHP?
As the Php Manual say,
print_r — Prints human-readable information about a variable
When we use json_decode();, we get an object of type stdClass as return type.
The arguments, which are to be passed inside of print_r() should either be an array or a string. Hence, we cannot pass an object inside of print_r(). I found 2 ways to deal with this.
Cast the object to array.
This can be achieved as follows.
$a = (array)$object;
By accessing the key of the Object
As mentioned earlier, when you use json_decode(); function, it returns an Object of stdClass. you can access the elements of the object with the help of -> Operator.
$value = $object->key;
One, can also use multiple keys to extract the sub elements incase if the object has nested arrays.
$value = $object->key1->key2->key3...;
Their are other options to print_r() as well, like var_dump(); and var_export();
P.S : Also, If you set the second parameter of the json_decode(); to true, it will automatically convert the object to an array();
Here are some references:
http://php.net/manual/en/function.print-r.php
http://php.net/manual/en/function.var-dump.php
http://php.net/manual/en/function.var-export.php
Try something like this one!
Instead of getting the context like:(this works for getting array index's)
$result['context']
try (this work for getting objects)
$result->context
Other Example is: (if $result has multiple data values)
Array
(
[0] => stdClass Object
(
[id] => 15
[name] => 1 Pc Meal
[context] => 5
[restaurant_id] => 2
[items] =>
[details] => 1 Thigh (or 2 Drums) along with Taters
[nutrition_fact] => {"":""}
[servings] => menu
[availability] => 1
[has_discount] => {"menu":0}
[price] => {"menu":"8.03"}
[discounted_price] => {"menu":""}
[thumbnail] => YPenWSkFZm2BrJT4637o.jpg
[slug] => 1-pc-meal
[created_at] => 1612290600
[updated_at] => 1612463400
)
)
Then try this:
foreach($result as $results)
{
$results->context;
}
To get an array as result from a json string you should set second param as boolean true.
$result = json_decode($json_string, true);
$context = $result['context'];
Otherwise $result will be an std object. but you can access values as object.
$result = json_decode($json_string);
$context = $result->context;
Sometimes when working with API you simply want to keep an object an object. To access the object that has nested objects you could do the following:
We will assume when you print_r the object you might see this:
print_r($response);
stdClass object
(
[status] => success
[message] => Some message from the data
[0] => stdClass object
(
[first] => Robert
[last] => Saylor
[title] => Symfony Developer
)
[1] => stdClass object
(
[country] => USA
)
)
To access the first part of the object:
print $response->{'status'};
And that would output "success"
Now let's key the other parts:
$first = $response->{0}->{'first'};
print "First name: {$first}<br>";
The expected output would be "Robert" with a line break.
You can also re-assign part of the object to another object.
$contact = $response->{0};
print "First Name: " . $contact->{'first'} . "<br>";
The expected output would be "Robert" with a line break.
To access the next key "1" the process is the same.
print "Country: " . $response->{1}->{'country'} . "<br>";
The expected output would be "USA"
Hopefully this will help you understand objects and why we want to keep an object an object. You should not need to convert an object to an array to access its properties.
You can convert stdClass object to array like:
$array = (array)$stdClass;
stdClsss to array
When you try to access it as $result['context'], you treating it as an array, the error it's telling you that you are actually dealing with an object, then you should access it as $result->context
Here is the function signature:
mixed json_decode ( string $json [, bool $assoc = false [, int $depth = 512 [, int $options = 0 ]]] )
When param is false, which is default, it will return an appropriate php type. You fetch the value of that type using object.method paradigm.
When param is true, it will return associative arrays.
It will return NULL on error.
If you want to fetch value through array, set assoc to true.
I got this error out of the blue because my facebook login suddently stopped working (I had also changed hosts) and throwed this error. The fix is really easy
The issue was in this code
$response = (new FacebookRequest(
FacebookSession::newAppSession($this->appId, $this->appSecret),
'GET',
'/oauth/access_token',
$params
))->execute()->getResponse(true);
if (isset($response['access_token'])) { <---- this line gave error
return new FacebookSession($response['access_token']);
}
Basically isset() function expect an array but instead it find an object. The simple solution is to convert PHP object to array using (array) quantifier. The following is the fixed code.
$response = (array) (new FacebookRequest(
FacebookSession::newAppSession($this->appId, $this->appSecret),
'GET',
'/oauth/access_token',
$params
))->execute()->getResponse(true);
Note the use off array() quantifier in first line.
instead of using the brackets use the object operator for example my array based on database object is created like this in a class called DB:
class DB {
private static $_instance = null;
private $_pdo,
$_query,
$_error = false,
$_results,
$_count = 0;
private function __construct() {
try{
$this->_pdo = new PDO('mysql:host=' . Config::get('mysql/host') .';dbname=' . Config::get('mysql/db') , Config::get('mysql/username') ,Config::get('mysql/password') );
} catch(PDOException $e) {
$this->_error = true;
$newsMessage = 'Sorry. Database is off line';
$pagetitle = 'Teknikal Tim - Database Error';
$pagedescription = 'Teknikal Tim Database Error page';
include_once 'dbdown.html.php';
exit;
}
$headerinc = 'header.html.php';
}
public static function getInstance() {
if(!isset(self::$_instance)) {
self::$_instance = new DB();
}
return self::$_instance;
}
public function query($sql, $params = array()) {
$this->_error = false;
if($this->_query = $this->_pdo->prepare($sql)) {
$x = 1;
if(count($params)) {
foreach($params as $param){
$this->_query->bindValue($x, $param);
$x++;
}
}
}
if($this->_query->execute()) {
$this->_results = $this->_query->fetchAll(PDO::FETCH_OBJ);
$this->_count = $this->_query->rowCount();
}
else{
$this->_error = true;
}
return $this;
}
public function action($action, $table, $where = array()) {
if(count($where) ===3) {
$operators = array('=', '>', '<', '>=', '<=');
$field = $where[0];
$operator = $where[1];
$value = $where[2];
if(in_array($operator, $operators)) {
$sql = "{$action} FROM {$table} WHERE {$field} = ?";
if(!$this->query($sql, array($value))->error()) {
return $this;
}
}
}
return false;
}
public function get($table, $where) {
return $this->action('SELECT *', $table, $where);
public function results() {
return $this->_results;
}
public function first() {
return $this->_results[0];
}
public function count() {
return $this->_count;
}
}
to access the information I use this code on the controller script:
<?php
$pagetitle = 'Teknikal Tim - Service Call Reservation';
$pagedescription = 'Teknikal Tim Sevice Call Reservation Page';
require_once $_SERVER['DOCUMENT_ROOT'] .'/core/init.php';
$newsMessage = 'temp message';
$servicecallsdb = DB::getInstance()->get('tt_service_calls', array('UserID',
'=','$_SESSION['UserID']));
if(!$servicecallsdb) {
// $servicecalls[] = array('ID'=>'','ServiceCallDescription'=>'No Service Calls');
} else {
$servicecalls = $servicecallsdb->results();
}
include 'servicecalls.html.php';
?>
then to display the information I check to see if servicecalls has been set and has a count greater than 0 remember it's not an array I am referencing so I access the records with the object operator "->" like this:
<?php include $_SERVER['DOCUMENT_ROOT'] .'/includes/header.html.php';?>
<!--Main content-->
<div id="mainholder"> <!-- div so that page footer can have a minum height from the
header -->
<h1><?php if(isset($pagetitle)) htmlout($pagetitle);?></h1>
<br>
<br>
<article>
<h2></h2>
</article>
<?php
if (isset($servicecalls)) {
if (count ($servicecalls) > 0){
foreach ($servicecalls as $servicecall) {
echo '<a href="/servicecalls/?servicecall=' .$servicecall->ID .'">'
.$servicecall->ServiceCallDescription .'</a>';
}
}else echo 'No service Calls';
}
?>
Raise New Service Call
</div> <!-- Main content end-->
<?php include $_SERVER['DOCUMENT_ROOT'] .'/includes/footer.html.php'; ?>
It is most likely when it tries to access the data with the generic bracket array accessor and not an object operator. Always ensure the variable type is before accessing the data.
While decoding the JSON, the response will be generated as stdObject instances
Rather than calling $result['context'];, access it by $result->context;
If it needs to call as an array itself, decode the JSON by passing the second parameter as true like
json_decode($jsonData, true);
Change it for
$results->fetch_array()
Is there a way to convert a multidimensional array to a stdClass object in PHP?
Casting as (object) doesn't seem to work recursively. json_decode(json_encode($array)) produces the result I'm looking for, but there has to be a better way...
As far as I can tell, there is no prebuilt solution for this, so you can just roll your own:
function array_to_object($array) {
$obj = new stdClass();
foreach ($array as $k => $v) {
if (strlen($k)) {
if (is_array($v)) {
$obj->{$k} = array_to_object($v); //RECURSION
} else {
$obj->{$k} = $v;
}
}
}
return $obj;
}
I know this answer is coming late but I'll post it for anyone who's looking for a solution.
Instead of all this looping etc, you can use PHP's native json_* function. I've got a couple of handy functions that I use a lot
/**
* Convert an array into a stdClass()
*
* #param array $array The array we want to convert
*
* #return object
*/
function arrayToObject($array)
{
// First we convert the array to a json string
$json = json_encode($array);
// The we convert the json string to a stdClass()
$object = json_decode($json);
return $object;
}
/**
* Convert a object to an array
*
* #param object $object The object we want to convert
*
* #return array
*/
function objectToArray($object)
{
// First we convert the object into a json string
$json = json_encode($object);
// Then we convert the json string to an array
$array = json_decode($json, true);
return $array;
}
Hope this can be helpful
You and many others have pointed to the JSON built-in functions, json_decode() and json_encode(). The method which you have mentioned works, but not completely: it won't convert indexed arrays to objects, and they will remain as indexed arrays. However, there is a trick to overcome this problem. You can use JSON_FORCE_OBJECT constant:
// Converts an array to an object recursively
$object = json_decode(json_encode($array, JSON_FORCE_OBJECT));
Tip: Also, as mentioned here, you can convert an object to array recursively using JSON functions:
// Converts an object to an array recursively
$array = json_decode(json_encode($object), true));
Important Note: If you do care about performance, do not use this method. While it is short and clean, but it is the slowest among alternatives. See my other answer in this thread relating this.
function toObject($array) {
$obj = new stdClass();
foreach ($array as $key => $val) {
$obj->$key = is_array($val) ? toObject($val) : $val;
}
return $obj;
}
You can use the array_map recursively:
public static function _arrayToObject($array) {
return is_array($array) ? (object) array_map([__CLASS__, __METHOD__], $array) : $array;
}
Works perfect for me since it doesn't cast for example Carbon objects to a basic stdClass (which the json encode/decode does)
/**
* Recursively converts associative arrays to stdClass while keeping integer keys subarrays as arrays
* (lists of scalar values or collection of objects).
*/
function a2o( array $array ) {
$resultObj = new \stdClass;
$resultArr = array();
$hasIntKeys = false;
$hasStrKeys = false;
foreach ( $array as $k => $v ) {
if ( !$hasIntKeys ) {
$hasIntKeys = is_int( $k );
}
if ( !$hasStrKeys ) {
$hasStrKeys = is_string( $k );
}
if ( $hasIntKeys && $hasStrKeys ) {
$e = new \Exception( 'Current level has both integer and string keys, thus it is impossible to keep array or convert to object' );
$e->vars = array( 'level' => $array );
throw $e;
}
if ( $hasStrKeys ) {
$resultObj->{$k} = is_array( $v ) ? a2o( $v ) : $v;
} else {
$resultArr[$k] = is_array( $v ) ? a2o( $v ) : $v;
}
}
return ($hasStrKeys) ? $resultObj : $resultArr;
}
Some of the other solutions posted here fail to tell apart sequential arrays (what would be [] in JS) from maps ({} in JS.) For many use cases it's important to tell apart PHP arrays that have all sequential numeric keys, which should be left as such, from PHP arrays that have no numeric keys, which should be converted to objects. (My solutions below are undefined for arrays that don't fall in the above two categories.)
The json_decode(json_encode($x)) method does handle the two types correctly, but is not the fastest solution. It's still decent though, totaling 25µs per run on my sample data (averaged over 1M runs, minus the loop overhead.)
I benchmarked a couple of variations of the recursive converter and ended up with the following. It rebuilds all arrays and objects (performing a deep copy) but seems to be faster than alternative solutions that modify the arrays in place. It clocks at 11µs per execution on my sample data:
function array_to_object($x) {
if (!is_array($x)) {
return $x;
} elseif (is_numeric(key($x))) {
return array_map(__FUNCTION__, $x);
} else {
return (object) array_map(__FUNCTION__, $x);
}
}
Here is an in-place version. It may be faster on some large input data where only small parts need to be converted, but on my sample data it took 15µs per execution:
function array_to_object_inplace(&$x) {
if (!is_array($x)) {
return;
}
array_walk($x, __FUNCTION__);
reset($x);
if (!is_numeric(key($x))) {
$x = (object) $x;
}
}
I did not try out solutions using array_walk_recursive()
public static function _arrayToObject($array) {
$json = json_encode($array);
$object = json_decode($json);
return $object
}
Because the performance is mentioned, and in fact it should be important in many places, I tried to benchmark functions answered here.
You can see the code and sample data here in this gist. The results are tested with the data exists there (a random JSON file, around 200 KB in size), and each function repeated one thousand times, for the results to be more accurate.
Here are the results for different PHP configurations:
PHP 7.4.16 (no JIT)
$ php -dopcache.enable_cli=1 benchmark.php
pureRecursive(): Completed in 0.000560s
pureRecursivePreservingIntKeys(): Completed in 0.000580s
jsonEncode(): Completed in 0.002045s
jsonEncodeOptimized(): Completed in 0.002060s
jsonEncodeForceObject(): Completed in 0.002174s
arrayMap(): Completed in 0.000561s
arrayMapPreservingIntKeys(): Completed in 0.000592s
arrayWalkInplaceWrapper(): Completed in 0.001016s
PHP 8.0.2 (no JIT)
$ php -dopcache.enable_cli=1 benchmark.php
pureRecursive(): Completed in 0.000535s
pureRecursivePreservingIntKeys(): Completed in 0.000578s
jsonEncode(): Completed in 0.001991s
jsonEncodeOptimized(): Completed in 0.001990s
jsonEncodeForceObject(): Completed in 0.002164s
arrayMap(): Completed in 0.000579s
arrayMapPreservingIntKeys(): Completed in 0.000615s
arrayWalkInplaceWrapper(): Completed in 0.001040s
PHP 8.0.2 (tracing JIT)
$ php -dopcache.enable_cli=1 -dopcache.jit_buffer_size=250M -dopcache.jit=tracing benchmark.php
pureRecursive(): Completed in 0.000422s
pureRecursivePreservingIntKeys(): Completed in 0.000410s
jsonEncode(): Completed in 0.002004s
jsonEncodeOptimized(): Completed in 0.001997s
jsonEncodeForceObject(): Completed in 0.002094s
arrayMap(): Completed in 0.000577s
arrayMapPreservingIntKeys(): Completed in 0.000593s
arrayWalkInplaceWrapper(): Completed in 0.001012s
As you see, the fastest method with this benchmark is pure recursive PHP functions (posted by #JacobRelkin and #DmitriySintsov), especially when it comes to the JIT compiler. When it comes to json_* functions, they are the slowest ones. They are about 3x-4x (in the case of JIT, 5x) slower than the pure method, which may seem unbelievable.
One thing to note: If you remove iterations (i.e. run each function only one time), or even strictly lower its count, the results would differ. In such cases, arrayMap*() variants win over pureRecursive*() ones (still json_* functions method should be the slowest). But, you should simply ignore these cases. In the terms of performance, scalability is much more important.
As a result, in the case of converting arrays to object (and vice versa?), you should always use pure PHP functions, resulting in the best performance, perhaps independent from your configurations.
The simpliest way to convert an associative array to object is:
First encode it in json, then decode it.
like $objectArray = json_decode(json_encode($associtiveArray));
Here's a function to do an in-place deep array-to-object conversion that uses PHP internal (shallow) array-to-object type casting mechanism.
It creates new objects only when necessary, minimizing data duplication.
function toObject($array) {
foreach ($array as $key=>$value)
if (is_array($value))
$array[$key] = toObject($value);
return (object)$array;
}
Warning - do not use this code if there is a risk of having circular references.
Here is a smooth way to do it that can handle an associative array with great depth and doesn't overwrite object properties that are not in the array.
<?php
function setPropsViaArray( $a, $o )
{
foreach ( $a as $k => $v )
{
if ( is_array( $v ) )
{
$o->{$k} = setPropsViaArray( $v, ! empty ( $o->{$k} ) ? $o->{$k} : new stdClass() );
}
else
{
$o->{$k} = $v;
}
}
return $o;
};
setPropsViaArray( $newArrayData, $existingObject );
Late, but just wanted to mention that you can use the JSON encoding/decoding to convert fully from/to array:
//convert object $object into array
$array = json_decode(json_encode($object), true);
//convert array $array into object
$object = json_decode(json_encode($array));
json_encode and json_decode functions are available starting from php 5.2
EDIT: This function is conversion from object to array.
From https://forrst.com/posts/PHP_Recursive_Object_to_Array_good_for_handling-0ka
protected function object_to_array($obj)
{
$arrObj = is_object($obj) ? get_object_vars($obj) : $obj;
foreach ($arrObj as $key => $val) {
$val = (is_array($val) || is_object($val)) ? $this->object_to_array($val) : $val;
$arr[$key] = $val;
}
return $arr;
}
I was looking for a way that acts like json_decode(json_encode($array))
The problem with most other recursive functions here is that they also convert sequential arrays into objects. However, the JSON variant does not do this by default. It only converts associative arrays into objects.
The following implementation works for me like the JSON variant:
function is_array_assoc ($arr) {
if (!is_array($arr)) return false;
foreach (array_keys($arr) as $k => $v) if ($k !== $v) return true;
return false;
}
// json_decode(json_encode($array))
function array_to_object ($arr) {
if (!is_array($arr) && !is_object($arr)) return $arr;
$arr = array_map(__FUNCTION__, (array)$arr);
return is_array_assoc($arr) ? (object)$arr : $arr;
}
// json_decode(json_encode($array, true))
// json_decode(json_encode($array, JSON_OBJECT_AS_ARRAY))
function object_to_array ($obj) {
if (!is_object($obj) && !is_array($obj)) return $obj;
return array_map(__FUNCTION__, (array)$obj);
}
If you want to have the functions as a class:
class ArrayUtils {
public static function isArrAssoc ($arr) {
if (!is_array($arr)) return false;
foreach (array_keys($arr) as $k => $v) if ($k !== $v) return true;
return false;
}
// json_decode(json_encode($array))
public static function arrToObj ($arr) {
if (!is_array($arr) && !is_object($arr)) return $arr;
$arr = array_map([__CLASS__, __METHOD__], (array)$arr);
return self::isArrAssoc($arr) ? (object)$arr : $arr;
}
// json_decode(json_encode($array, true))
// json_decode(json_encode($array, JSON_OBJECT_AS_ARRAY))
public static function objToArr ($obj) {
if (!is_object($obj) && !is_array($obj)) return $obj;
return array_map([__CLASS__, __METHOD__], (array)$obj);
}
}
If anyone finds any mistakes please let me know.
/**
* Convert a multidimensional array to an object recursively.
* For any arrays inside another array, the result will be an array of objects.
*
* #author Marcos Freitas
* #param array|any $props
* #return array|any
*/
function array_to_object($props, $preserve_array_indexes = false) {
$obj = new \stdClass();
if (!is_array($props)) {
return $props;
}
foreach($props as $key => $value) {
if (is_numeric($key) && !$preserve_array_indexes) {
if(!is_array($obj)) {
$obj = [];
}
$obj[] = $this->array_to_object($value);
continue;
}
$obj->{$key} = is_array($value) ? $this->array_to_object($value) : $value;
}
return $obj;
}
The shortest I could come up with:
array_walk_recursive($obj, function (&$val) { if (is_object($val)) $val = get_object_vars($val); });