Get your FREE 30 page Developing SOLID Applications guide!

A Closer Look At ArrayObject

Out Of Date Warning

Languages change. Perspectives are different. Ideas move on. This article was published on April 26, 2010 which is more than two years ago. It may be out of date. You should verify that technical information in this article is still current before relying upon it for your own purposes.

Every once in a while I discover a really cool, really underutilized component in PHP and I just have to write about it. I recently discovered (through my use of Zend Framework) the implementation of the ArrayObject class.

ArrayObject is an object that is designed to behave exactly like an array. If that seems confusing, don’t worry, it’s not. ArrayObject is an object. It follows all the rules of how objects work. But it’s designed to implicitly behave like an array for all intents and purposes, including being used in a foreach loop, and accessing it’s properties just like you would access the values in an array. Consider the following code sample:

<?php

$array = array('one', 'two', 'three');

$arrayObj = new ArrayObject($array);

var_dump(($array[0] == $arrayObj[0])); // Outputs true.

?>

This might not seem too terribly exciting; in fact, any of the SPL classes that implement ArrayAccess and IteratorAggregate will exhibit this behavior. You can typically access an object’s properties with a foreach loop. But the key here is the fact that ArrayObject gives you the flexibility of an array along with the power of an object in PHP – something that makes it very interesting.

The way objects are used in PHP 5, assigning them doesn’t copy them; it copies their location in a lookup table for later retrieval. This means that an object modified in one area of the application reflects those changes everywhere it has been passed; there is no need to do any extra work to ensure that code using your objects has those changes. Arrays, on the other hand, are copied on assignment, meaning that changes made in one component of the application will not be reflected in the original array, unless you passed the array by reference. But because ArrayObject behaves like an array while still following the rules of an object, it has a number of distinct advantages.

It’s most obvious advantage is the ability to reflect changes throughout an application, avoiding the need for copying an array, modifying it, and then returning it for reassignment, or the ever-dreaded passing by reference option. And the ArrayObject, part of the SPL, offers some performance improvements over conventional coding options. Finally, the ArrayObject allows developers to be more purist in their object-oriented implementations will still gaining the flexibility and structure of arrays.

This object is not without it’s drawbacks however: there are a number of features that cannot be used with this object, including most of the array_* functions in the PHP library. This can be a significant drawback to those skilled with the use of arrays, though developers can extend the class and implement some of this behavior themselves.

Still, ArrayObject is being used in lots of PHP applications, including Zend Framework (Zend_Registry extends ArrayObject, for example). It’s a great SPL component, and one I’ll be making more more use of in the future.

Write better object oriented PHP today.

Object oriented programming always leaves you with a headache. What if you could master it instead?

Get the book now! »

Andrei (@a) wrote at 4/26/2010 11:29 am:

I don’t see how you can say both that ArrayObject “is designed to behave exactly like an array” and that you cannot use it with majority of functions that accept arrays. Pick one.

Brandon Savage (@brandonsavage) wrote at 4/26/2010 11:37 am:

I point you to the documentation itself (http://php.net/manual/en/class.arrayobject.php) which states, ” this class allows objects to work as arrays.”

If I had my way I’d have all the array functions take ArrayObjects and treat them like arrays; I voted up the change request put in by Christian Wenske.

Andrei (@a) wrote at 4/26/2010 12:11 pm:

I know what the docs say, but you’re not rehashing the documentation here, you’re writing an article. If anything, it would be helpful if you pointed out in your post exactly what your previous comment said.

Roland wrote at 4/26/2010 1:17 pm:

You say “Arrays […] are copied on assignment”. That’s wrong.
Arrays are copied on write.

Besides that ArrayObject is not supported by PHP’s array-functions, it does not pass an array-typehint (nor does an array pass an ArrayObject-typehint).

IMHO ArrayObject is massively overestimated (and – as a concrete class – depending on it should be avoided).

Maarten wrote at 4/26/2010 3:17 pm:

Andrei: you’re right but your tone is wrong.

Brandon Savage (@brandonsavage) wrote at 4/27/2010 7:15 am:

Roland, I’m not entirely sure why we’re arguing the semantics of “copy-on-write”/”copy on assignment”/”assignment by value” when really we’re talking about behavior: the fact that, when you execute $a = $b, the value of $b is copied, and then assigned to $a. It doesn’t matter if $b is a string, an integer, an array or an object; the value of $b is assigned to $a.

Your other points about ArrayObject are valid; you would have to typehint for ArrayObject instead of Array. There are inconsistencies in the PHP API, which are extremely frustrating but a part of how PHP works.

Andrei, I’m not sure what you’re looking for here. The 6th paragraph does in fact express that there is a ticket, and explains that the array_* functions don’t work as you would expect. I’m not about to advocate that people go vote up a ticket just because I’d like to see it resolved; that’s not professional in my opinion.

Roland wrote at 4/27/2010 8:00 am:

“when you execute $a = $b, the value of $b is copied, and then assigned to $a. It doesn’t matter if $b is a string, an integer, an array or an object; the value of $b is assigned to $a.”

That’s not correct:
If $b is an object, $a = $b does not copy the object, but copies the reference to the object.
If $b is an array, $a = $b does not copy the array, but copies the reference to the array. Then, when either $a or $b is writing to the array, the array is copied:
<?php
$foo = array();
$bar = $foo; // both variables referencing the same array

// reading the array through $bar or $foo reads the same array!

// copy on write:
$bar[] = '';
// now there are two arrays in memory,
// one for $foo; one for $bar.

Brandon Savage (@brandonsavage) wrote at 4/27/2010 8:05 am:

I’ve said this at least four times in previous posts. Objects are NOT passed by reference.

http://blog.libssh2.org/index.php?/archives/51-Youre-being-lied-to..html

So what you’re arguing here is that PHP handles arrays in a particular fashion, and only duplicates the data when you read/write to the array? So the bottom line behavior is the same? Sounds like we’re arguing semantics.

Roland wrote at 4/27/2010 9:30 am:

Don’t know what you said in previous posts.

Here you say:
“when you execute $a = $b, the value of $b is copied, and then assigned to $a”. But this is correct for scalar values only, and is wrong for objects, resources and arrays.
I said, “$a = $b does not copy the object, but copies the reference to the object”. The article you link to is nice, but does not cure the flimsiness of your blogpost.

Call it “semantics”. Talking about content, meaning and understanding. People too often tend to ignore it. I can’t see, how copy-on-write and copy-on-assignment could be considered to share the same “bottom line behaviour”.

Artem wrote at 4/27/2010 10:52 am:

Did you delete my post? wtf?

Brandon Savage (@brandonsavage) wrote at 4/27/2010 10:54 am:

This is the first comment from you, sir. I can’t delete what I never saw. Links, URLs, HTML and other items are caught by the spam filter.

Wil Moore III (@wilmoore) wrote at 4/28/2010 4:17 am:

Nice post Brandon.

I recently (2 weeks ago) utilized ArrayObject in a project. Works very well for certain use cases. I hear some of the negatives presented in the comments. Right, array_* functions don’t work. OK, so…use ArrayObject for what it is useful for and skip it when it fails to be useful :)