tags:

views:

99

answers:

3

I've got some very weird behavior in my PHP code. I don't know if this is actually a good SO question, since it almost looks like a bug in PHP. I had this problem in a project of mine and isolated the problem:

// json object that will be converted into an array
$json = '{"5":"88"}';
$jsonvar = (array) json_decode($json); // notice: Casting to an array
// Displaying the array:
var_dump($jsonvar);
// Testing if the key is there
var_dump(isset($jsonvar["5"]));
var_dump(isset($jsonvar[5]));

That code outputs the following:

array(1) {
  ["5"]=>
  string(2) "88"
}
bool(false)
bool(false)

The big problem: Both of those tests should produce bool(true) - if you create the same array using regular php arrays, this is what you'll see:

// Let's create a similar PHP array in a regular manner:
$phparr = array("5" => "88");
// Displaying the array:
var_dump($phparr);
// Testing if the key is there
var_dump(isset($phparr["5"]));
var_dump(isset($phparr[5]));

The output of that:

array(1) {
  [5]=>
  string(2) "88"
}
bool(true)
bool(true)

So this doesn't really make sense. I've tested this on two different installations of PHP/apache.

You can copy-paste the code to a php file yourself to test it.

It must have something to do with the casting from an object to an array.

+5  A: 

Use the json_decode function parameters to get an array, instead of changing an object yourself.

json_decode ( string $json [, bool $assoc = false])
$assoc - When TRUE, returned object s will be converted into associative array s.

Your code, when changed to

$jsonvar = json_decode($json, true);

works as expected

Joel L
This is true, but I would still like to know why it doesn't match the key, no matter what the origin of the array is.
arnorhs
+2  A: 

It can be further simplified to this problem

$o = new stdClass;
$o->{5} = 1; //or even $o->{'5'} = 1;
$a = (array) $o;
print_r($a);
var_dump(isset($a[5]));
var_dump(isset($a['5']));

It seems that this only happens when the property name is one that php would normally consider a numeric key in an array.

Definitely unexpected behavior to me.


edit Same issue here http://stackoverflow.com/questions/1869812/casing-an-array-with-numeric-keys-as-an-object


edit#2 documented behavior http://www.php.net/manual/en/language.types.array.php#language.types.array.casting

chris
It's documented but still a bit annoying. For arrays php always tests whether the key is somehow numeric, e.g. $a['5'] will treat 5 as a numeric key (and marks the bucket for the hashtable with keylength=0). Objects on the other hand treat numerical indices always as string, e.g. $o->{5} creates a string key in the properties hashtable of that object. When an object is cast to array the properties hashtable is more or less copied as-is, i.e. the behavior of the keys isn't converted as well. That's probably a speed optimization.
VolkerK
Ya I noticed some of that when digging through the source for zend_hash.c a while back. A dev did state they aren't changing it due to performance reasons http://bugs.php.net/bug.php?id=45959
chris
+2  A: 

Hello,

As chris said, you are hitting one of the limits of the json <-> object <-> array mappings.

Another interesting one to keep in mind is when a key in a json object is the empty string.

json = '{"":"88"}';

the key will be mapped to php as "empty" !

Jerome WAGNER
interesting tip
arnorhs