Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

get_object_vars returning different results depending on PHP version

Tags:

php

This isn't really a surprise that function implementations change sometimes from version to version, but not like this... Look:

$array = ["abc","def"];
$object = new stdclass();
foreach($array as $index => $value) {
    $object->$index = $value;
}
var_dump(get_object_vars($object));

For 5.6.x and then e.g. 7.0.17 and 7.1.3 we get:

array(2) { 
[0]=> string(3) "abc" 
[1]=> string(3) "def" 
}

But for 7.0.0 and 7.0.16 and 7.1.0 we get:

array(2) { 
["0"]=> string(3) "abc" 
["1"]=> string(3) "def" 
} 

demo: https://3v4l.org/jog4A

See? The keys are integers OR strings, depending on version.

Why? What is the reasoning behind those changes? And why isn't this documented anywhere? Or... is it?

like image 946
konrados Avatar asked Jun 30 '26 13:06

konrados


1 Answers

If you look more closely at the versions on that 3v4.org output, it was broken in 7.0.0 up to and including 7.0.16, and 7.1.0 up to and including 7.1.2. So this was a bug introduced in 7.0, and fixed on both active releases in time for 7.0.17 and 7.1.3 (both release March 16 2017).

Looking at the PHP changelog we can see a relevant looking entry:

Fixed bug #73998 (array_key_exists fails on arrays created by get_object_vars).

This leads us to the bug tracker, and from there to commit dd9cf23457e21d2bda29dc92d437b9dbd14027b2 in th git repo:

BUG #73998: Numeric properties are not accessible from get_object_vars

The fix involves adding a check for if there are numeric keys present, and skipping a block labelled "fast_copy" if there are.

So this was an undesired side-effect of a performance optimisation made during the development of PHP 7, and has now been fixed in all supported releases.

Interestingly, Andrea commented on the bug report that it is closely related to an RFC to change the behaviour of object-to-array casts which describes the general problem:

Because arrays and objects have different restrictions versus the underlying HashTable type on what kinds of keys they can have, the Zend Engine must enforce their restrictions at a layer above HashTables, in the code implementing arrays and objects themselves. This means that if that code is bypassed and the underlying HashTables are modified directly, arrays and objects can exist with an invalid internal state.

And the specific case addressed by the RFC:

For example, $arr = [0 => 1, 1 => 2, 2 => 3]; $obj = (object)$arr; produces an object with inaccessible properties named , 1 and 2, while $obj = new stdClass; $obj->{'0'} = 1; $obj->{'1'} = 2; $obj->{'2'} = 3; produces an array with the inaccessible keys "0", "1" and "2". The same issue also occurs when using get_object_vars().

The RFC is implemented in 7.2.0, as it changes documented behaviour, but the behaviour of get_object_vars() was actually an inadvertent change in 7.0, so was implemented as a bug fix.

like image 96
IMSoP Avatar answered Jul 02 '26 02:07

IMSoP