PHP quirks: a caveat with array string keys and an 'array_shift' call

Tags: ,

What do you think the following code snippet prints?

// Fill the array (each array element has the same value as a key)
foreach(array('07', '08', '09', '10') as $n){
    $a[$n] = $n;
}

// Delete the first element from the array
array_shift($a);

// Print a value of the element with a key of '10'
echo $a['10'];

If you, like me, think that the element of the array with a key of '10' has a string value of "10", then you're unfortunately wrong. But now you've got a reason to read the article further.

In fact, by the moment of executing the 'echo' operator the key of '10' will be absent in the array. PHP will notify us about this if you run the script (of course, only if E_NOTICE bit is active in error_reporting setting):

Notice: Undefined index: 10 in E:\string-keys-caveat.php on line 8

So, where is the needed element? Let's take a look at the array's content (with the help of var_dump($a)) and try to understand what happens.

Here is what we have in the array just after the array_shift call:

array(3) {
 ["08"] => string(2) "08"
 ["09"] => string(2) "09"
 [0]    => string(2) "10"
}

The key of '10' is really absent. But the key of '0' has a value of "10", and exactly that value we expect to see printed with `echo $a['10']` call. Because in the foreach loop we've filled the array with unique values equal to the keys, we can say that the key of '0' is the former key of '10'.

OK, but just after the loop the key of '10' must be in the array, right? Let's execute var_dump($a) immediately after foreach, but before array_shift and check:

array(4) {
 ["07"] => string(2) "07"
 ["08"] => string(2) "08"
 ["09"] => string(2) "09"
 [10]   => string(2) "10"
}

As we see, the needed key is in place, but it has a trait in which it differs from the other keys of the aray. All keys are string keys, but the key of '10' is not. Although we passed "10" as a string, PHP converted it to a number. That's because "10", in contrast to "07", "08" and "09", is the standard representation of a number. Here is a quote from the PHP manual about that:

A key may be either an integer or a string. If a key is the standard representation of an integer, it will be interpreted as such (i.e. "8" will be interpreted as 8, while "08" will be interpreted as "08").

http://www.php.net/manual/en/language.types.array.php

Usually nobody notices that conversion, because it's possible to access numeric keys in the same way as string keys (echo $a['10']). Thus a programmer stays in ignorance of that pitfall, unless an array is implicitly reindexed. The latter happens when one calls array_shift function – it not only deletes the first element of an array, but changes all numeric keys to be ordered (e.g. "0, 1, 2...").  That's why in our snippet after executing `array_shift($a)` the key of '10' becomes the key of '0' because it is the only numeric key in the array.

Well, you've now learned about just another PHP quirk. I think the code snippet at the top of the article could be used as a PHP brainteaser, included into a quiz or an interview.

PS: everything's fine, but still one question remains – how to force a string that looks like a number to stay a string as an array key (tricks like `$a[(string) "10"] = 'hi!'` doesn't help). I understand it's rarely needed, but I'm just curious.


24 January 2012

Comments (frozen for the time being)

No one has commented on this page yet.


Интернет реклама