1

I'm running PHP7.0.9, when I do something like this:

$s = json_decode('{"1": "xxx"}');//decode json to stdClass
$a = (array)$s;//cast to array
die(var_dump($a, $a['1'], $a[1], count($a)));

I get this result:

array (size=1)
  '1' => string 'xxx' (length=3) //<-- key 1 exists
null // $a['1'] yields null
null // $a[1] doesn't work either
int 1 // count shows there is 1 element in the array

I was expecting this result:

array (size=1)
  '1' => string 'xxx' (length=3)
string 'xxx' (length=3) // $a['1'] should work
null
int 1

My question: Why can't I access $a['1'], even though both countand a var_dump of the array tell me that this key exists? Is this a bug in PHP, or some kind of feature?

Elias Van Ootegem
  • 74,482
  • 9
  • 111
  • 149
maxgu
  • 137
  • 1
  • 11

1 Answers1

7

Quoting the PHP documentation:

Array to object

If an object is converted to an object, it is not modified. If a value of any other type is converted to an object, a new instance of the stdClass built-in class is created. If the value was NULL, the new instance will be empty. An array converts to an object with properties named by keys and corresponding values, with the exception of numeric keys which will be inaccessible unless iterated.

Object to Array

And similar issues/quirks present themselves when converting the other way around: (documentation)

If an object is converted to an array, the result is an array whose elements are the object's properties. The keys are the member variable names, with a few notable exceptions: integer properties are unaccessible; private variables have the class name prepended to the variable name; protected variables have a '*' prepended to the variable name. These prepended values have null bytes on either side. This can result in some unexpected behaviour:

Basically, objects with numeric properties can be cast to an array, and you can iterate it, but the keys are inaccessible (directly). It's a known quirk. You can get around it by either using json_decode($string, true); to convert to an array right off the bat, or use a secondary loop to "reconstruct" the array:

$reconstructed = [];
foreach ((array) $obj as $k => $v) {
    $reconstructed[$k] = $v;
}

Whether this is a bug or a feature is unclear. When I first encountered this behaviour, I called it a bug. Given that it's documented and a known quirk, I'd now say it's neither. It's not really a bug because it's known, understood and documented, but it's hardly a feature. It's just one of those messy quirks that most languages have.

You'll have to work around it, live with it, avoid it, and deal with it. Given that there's been many bugreports on the PHP mailing lists about this, and it's added to the documentation, it's probably something that is unlikely to get fixed any time soon.

Elias Van Ootegem
  • 74,482
  • 9
  • 111
  • 149
  • 1
    That's about converting from an array to an object. He's going the other way. – Barmar Aug 18 '16 at 15:47
  • @Barmar: Added a reference to the documentation for object to array conversion, too – Elias Van Ootegem Aug 18 '16 at 15:50
  • That's one of the weirdest quirks I've ever seen in PHP. I understand the motivation for the nulls around private and protected properties, but can't imagine the reason for making integer properties inaccessible. – Barmar Aug 18 '16 at 15:53
  • @Barmar: I know, it's absurd in every possible sense of the word. I remember looking at the zend engine code, to see how it's implemented and why it behaves in the way that it does. I couldn't quite work out what was going on, but I've never really gotten to the bottom of it all – Elias Van Ootegem Aug 18 '16 at 15:56
  • All I can think is that it's not putting the key into the hash map for some reason. So it can only find those elements when it scans the array sequentially. – Barmar Aug 18 '16 at 15:58
  • @Barmar: I remember seeing something like that happening. Either the bucket or the hash map was being linked to the property maps of the object. Given that `stdClass` properties aren't declared beforehand, they end up in that `properties` hash table, and not the `properties_table` `zval **` member, something bizarre is going on – Elias Van Ootegem Aug 18 '16 at 16:01