Strange Arrays PHP Puzzler: Dynamic typing, strings, and array indexes

May 20 2009

Take a look at this snippet of code:

<?php
$a = array('first');
if (isset($a[0]['some string'])) {
  print "What?" . PHP_EOL;
}
?>

What does it do? By all appearances, it declares an array ($a) with one value in it (first). Then it checks to see if $a's first entry has an index some string.

If it does find such an index, it prints What?. Otherwise the program silently exits.

So what happens when you run this code? If you give it a try, you may be surprised to see that it prints What? every time. How come? After all, we would expect that in order for isset() to return true, we would need an array that looked something like this:

<?php
$a = array(
  array('some string' => 1),
);
?>

In the case above, we create an array that has an array at the first index position. We could certainly reference $a[0]['some string'] in this example!

But why is the array array('first') evaluating to TRUE?

The reason is somewhat related to an early post here about how PHP deals with characters and strings in array contexts. What appears to be happening is this:

$a[0] returns the string some string. When $a[0]['some string'] is evaluated, PHP converts some string to a numeric value. Effectively, it is evaluated as (int)'some string', which will always evaluate to 0.

So $a[0]['some string'] is essentially performing the same task as $a[0][0]. And in this case, $a[0] is referencing the, and the last [0] is interpreted as being a reference to the first character in the string first

With this behind us, we can now make a trivial change to our sample code and see how this all works:

$a = array('first');
if (isset($a[0]['some string'])) {
  // print "What?" . PHP_EOL;
  print $a[0]['some string'] . PHP_EOL;
}
?>~~~


Now instead of printing <code>What?</code>, we are going to print the actual value of <code>$a[0]['some string']</code>. 

Given what we now know, we should be able to predict the output of the code above. If <code>$a[0]['some string']</code> is interpreted as <code>$a[0][0]</code>, and the second index is calculated against the string in the array, then what we would expect is that the first letter of the first item in the array would be printed.

In fact, that is what happens. Running the code above will output the letter <code>f</code>, which is the first character in <code>$a[0]</code>.

---
Special thanks to Heyrocker for finding this interesting tidbit.
<!--break-->