Converting Objects and Arrays

Unidimensional arrays and objects can be converted by casting (e.g. $array = (array) $object; / $object = (object) $array;) but if multidimensional only the first tier is converted. Here’s the solution!

You could iterate through each array item / object property and cast it, then recursively check if it has children which need casting too, but PHP’s JSON functions offer an easier way.

The following array will be our test data:

$array = [
    'foo' => true,
    'bar' => [
        'fizz' => 12,
        'buzz' => 'ban',
    ],
];

Why casting doesn’t work

Casting only changes the variable itself, but in a multidimensional array/object the items/properties are variables in themselves which is why casting doesn’t work. For example, running var_dump((object) $array); only yields:

object(stdClass)[1]
  public 'foo' => boolean true
  public 'bar' =>
    array (size=2)
      'fizz' => int 12
      'buzz' => string 'ban' (length=3)

The array has been converted to an object but bar is still an array!

Converting an Array to an Object

This is the easiest conversion — json_encode() converts an array to json and json_decode() converts json to a stdClass object:

$object = json_decode(json_encode($array));

var_dump()ing the above yields:

object(stdClass)[1]
  public 'foo' => boolean true
  public 'bar' => 
    object(stdClass)[2]
      public 'fizz' => int 12
      public 'buzz' => string 'ban' (length=3)

Note how all levels have been converted —a multidimensional array has become a multidimensional object! What’s happening is very simple: $array is converted to json with json_encode(), then that json is converted to an object with json_decode().

Converting an Object to an Array

This is a slightly different case in which we can take advantage of json_decode()‘s second param $assoc. It defaults to false and, according to the manual, “When TRUE, returned objects will be converted into associative arrays”:

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

var_dump()ing the above yields:

array (size=2)
  'foo' => boolean true
  'bar' =>
    array (size=2)
      'fizz' => int 12
      'buzz' => string 'ban' (length=3)

Again, note how all levels have been converted. What’s happening this time is that $object is converted to json with json_encode() like before, but this time we convert that json into an associative array with json_decode() and passing true as its second parameter.

Merge Multidimensional Objects

A problem I came across recently involved having to merge two multidimensional objects, both containing other objects, strings, integers and booleans. One object contained user settings and the other contained the default settings; merging the two (overwriting the defaults with the user settings) would ensure the correct settings were used and that all settings had a variable — even if it had been added during an update and the user hadn’t saved a value for it!

The solution is to ensure both objects are arrays, then use array_replace_recursive() to merge the two. It replaces array keys with those from subsequent arrays, i.e. array in param 1 will be overwritten by those in param 2 if the keys are the same (array_merge(), recursively). So if you pass a defaults array and a user settings array, the result will contain every key, prioritising user settings.


// Array of default settings
$defaults = [];

// Associative array of user settings from stored json
$options = json_decode($user_settings_json, true);

// Merge defaults & options, prioritising options
$options = array_replace_recursive($defaults, $options);

// Convert options back to an object
$options = json_decode(json_encode($options));

Simple, no?

Conclusion

Multidimensional anythings are a strange beast but are brilliant at modelling complicated data structures. I’d call them a “necessary evil” but I don’t think they’re evil at all, just a bit tricky at times.

If you’ve been using serializeunserialize to convert variables into storable/transferable strings,  try using json instead. You won’t get a headache when linebreaks yield anomalous errors and you’ll be better-prepared if an API comes into play!


One final thing: json_encode() has its own trick up its sleeve —if you pass JSON_PRETTY_PRINT as a second parameter you get unminified json with spaces and newlines and everything! Just make sure you wrap it in <pre> for maximum win:

<pre><?=json_encode(array('foo', 'bar'), JSON_PRETTY_PRINT);></pre>

Leave a Reply

Your email address will not be published. Required fields are marked *