• Articles
  • The Strange Case of ArrayObject

When upgrading a website from one version of PHP to the next, there is one phrase that inevitably strikes fear into the heart of even the most intrepid PHP developer: Backwards Compatible Breaks. This article explores key differences in how our friendly SPL (Standard PHP Library) class ArrayObject is treated in PHP 8.

  • Post:Doug Bierer
  • 15 Feb 2020
  • Read 9
  • Shared 9
  • Comments 5

When upgrading a website from one version of PHP to the next, there is one phrase that inevitably strikes fear into the heart of even the most intrepid PHP developer: Backwards Compatible Breaks. There … I’ve said it. Might as well be screaming Voldemort, Voldemort, Voldemort at the top of my lungs in Hogwarts, right? (Harry Potter fans take note!) OK … but what does this have to do with The Strange Case of ArrayObject? To explain, let’s first have a look at ArrayObject and get_object_vars().

Demo Environment

To test my examples, I spun up a Linux for PHP docker container based upon PHP 7.1. I then ran lfphp-compile 8.0, modifying the script to compile PHP 8 into the /usr/local directory structure. I then linked PHP 7.1 into /usr/bin/php7 and PHP 8 into /usr/bin/php8. Here is the Dockerfile used:

FROM asclinux/linuxforphp-8.2-ultimate:7.1-nts
	echo "Compiling PHP 8 ..." && \
	cp /bin/lfphp-compile /bin/lfphp-compile-php8 && \
	sed -i 's/8.0.0dev/8.0.0beta3/g' /bin/lfphp-compile-php8 && \
	sed -i 's/--prefix=\/usr/--prefix=\/usr\/local --with-ffi --with-zip/g' /bin/lfphp-compile-php8 && \
	sed -i 's/git clone https:\/\/\/php\/php-src/git clone https:\/\/\/php\/php-src --branch PHP-8.0 \/root\/php-src/g' /bin/lfphp-compile-php8 && \
	/bin/lfphp-compile-php8 && \
	ln -sfv /usr/bin/php /usr/bin/php7 && \
	ln -sfv /usr/local/bin/php /usr/bin/php8
	echo "Enable display errors and configure php.ini for modifications later ..." && \
	sed -i 's/display_errors = Off/display_errors = On/g' /etc/php.ini && \
	sed -i 's/display_startup_errors = Off/display_startup_errors = On/g' /etc/php.ini && \
	sed -i 's/error_reporting =/;error_reporting =/g' /etc/php.ini && \
	echo "error_reporting = E_ALL" >>/etc/php.ini && \
	cp /etc/php.ini /tmp/php.ini && \
	chown apache:apache /etc/php.ini &&  \
	chmod 664 /etc/php.ini
CMD lfphp
Enter get_object_vars()

Although its use is hotly contested by many reputable developers, personally I really like get_object_vars(). There are caveats to its use, of course. For example, if used from the outside, it can only read public properties. Otherwise, it's extremely simple to use, and is an excellent tool for converting object properties into an associative array.

In the simple code block shown here, an ArrayObject instance is created and initialized with an array. Running getArrayCopy() and get_object_vars() should return the same results … right?

// core_oop_array_obj_1.php
$obj = new ArrayObject(['A' => 1,'B' => 2,'C' => 3]);

As expected, when running this example under PHP 7.1, identical results were obtained:

Whistling happily to myself, I then ran the exact same code under PHP 8. I then had to rub my eyes, clean my glasses (not done often enough, at least according to my wife!), and look again. It seems there’s something missing … Have a look for yourselves!

At this point, as you may have suspected, I stopped whistling. Uh … hmmm … NOTE TO SELF: after upgrading to PHP 8, when working with ArrayObject, make sure you use getArrayCopy() and NOT get_object_vars() !
See for yourself: core_oop_array_obj_1.php

It Gets Worse!

BC breaks … BC breaks … I kept mumbling to myself, they’re normal, right? even expected, right?. Taking a deep breath, I barreled ahead with my test. Thinking back to the countless times I’ve used get_object_vars(), I created another simple example. How many of you have done something like the getVars() method shown here? (No, don’t answer that question: I don’t want to be the only one in the room to raise my hand!)

// core_oop_array_obj_2.php
class Test extends ArrayObject
    public $id = 12345;
    public $name = 'Andrew Caya';
    public function getVars()
        return get_object_vars($this);

Now, granted, when extending ArrayObject, even running versions of PHP earlier than 7.4, you can expect the unexpected. Putting the above class to use, here is an example code block:

$test = new Test(['A' => 1,'B' => 2,'C' => 3]);

So, again, a class that extends ArrayObject is initialized with an array. Now here’s where it gets even more strange. What you would expect is that get_object_vars() would return both $id as well as $name, in addition to whatever was created upon instantiation, right? Wrong! Here is the output from PHP 7.1:

And the output from PHP 8:

On second glance, the output from PHP 8 seems to make more sense. If we run get_object_vars() internally, we would expect it to pick up the internally defined properties. On the other hand, getArrayCopy() returns the value of the array created upon initialization. Thus, to get both, the getVars() method would just need to combine the two as shown in this example:

public function getVars()
    return array_merge(

See for yourself: core_oop_array_obj_2.php

Got It … Anything Else?

Well … now that you ask … yes, there is something else, and please stop moaning and pounding your desk won’t you? The changes internal to PHP 8 that resulted in the behavior demonstrated above also affects array navigation functions such as reset(), current() and next(). In case you haven’t used these functions extensively, they’re “old school” ways of navigating arrays. reset() moves the array pointer to the “top” of the array (i.e. first element), current() returns the current value, and next() moves the array pointer to the next element. As before, we start with a simple ArrayObject instance, initialized with an associative array. We then use the "traditional" way of navigating arrays, using reset(), current() and next():

// core_oop_array_obj_3.php
$obj = new ArrayObject(['A','B','C']);
while ($item = current($obj)) {
    echo $item . ' ';

And here is the same thing, using iteration methods instead:

$it = $obj->getIterator();
while ($item = $it->current()) {
	echo $item . ' ';

As you probably expected, given that you’ve read this article up to this point, the results in PHP 7.1 are identical. However ... PHP 8 returns NULL as output from the first loop because, as you might also have expected, the traditional array navigation functions don’t work against a PHP 8 ArrayObject instance:
See for yourself: core_oop_array_obj_3.php


Recognition of potential code breaks is half the battle. As long as you are aware of potential breaks, you know what to look for potential breaks in your application post-upgrade. As you have seen from the test code discussed in this post, using the internal methods will return results consistent with versions of PHP 7. You can still use get_object_vars(), however you need to be aware that the internally generated storage array is treated differently in PHP 8. This means that it may be necessary to mix the results of get_object_vars() with getArrayCopy() to arrive at the desired results.

All in all, it’s extremely important to bear in mind that a PHP 8 upgrade is definitely a good thing. Performance has been improved, and there are many many enhancements that give you greater control over your code. To name just one: PHP 8 includes property level types. These and other improvements will be addressed here in future posts. SHAMELESS PLUG: we offer an intensive PHP 8 update course that covers not only backwards compatibility breaks, but also infrastructural changes, language enhancements, and other really cool stuff. Thanks for reading and Happy Coding!

Important Note: the changes discussed with regards to ArrayObject are also visible if you upgrade to PHP 7.4.

  Prove you're human! Enter the characters below: