Go to navigation

The danger of id numbers as keys in PHP arrays

Pelle Wessman - 15 september

Putting id numbers as the key in an array can look like a pretty smart decision at first - but it certainly has its pitfalls.

PHP considers numeric keys as array indexes - if you eg. do an array_merge() on an array the index will be rebuilt and start from 0 again - no matter if your keys really was an index or actually was something else like id numbers.

Take an example like this:

$nodes = array(
  13 => 'Python - beauty of nature or geeks?',
  25 => 'Interesting win in football',
  11 => 'BREAKING: Mona Lisa stopped smiling',
);
$imported_nodes = array(
  26 => 'Greenland\'s best beaches: We\'ve the list',
  27 => 'Motorist in crash with ideology',
);
$save = array_merge($nodes, $imported_nodes);

I would expect _$save_ to contain a bunch of node id:s linked to node titles - but only the titles and the order of the titles are preserved, the index has been rebuilt. A var_dump() gives us this:

array(5) {
  [0]=>
  string(35) "Python - beauty of nature or geeks?"
  [1]=>
  string(27) "Interesting win in football"
  [2]=>
  string(35) "BREAKING: Mona Lisa stopped smiling"
  [3]=>
  string(40) "Greenland's best beaches: We've the list"
  [4]=>
  string(31) "Motorist in crash with ideology"
}

A solution to this would be to use a foreach-loop instead of array_merge() to merge the two arrays because then the index wouldn't be rebuilt:

$save = $nodes;
foreach ($imported_nodes as $key => $value) {
  $save[$key] = $value;
}

I ran across into this behavior repeatedly when I was working with some tree-generation for an upcoming patch to the Drupal module Domain Relationships. In Drupal when you use the form element type 'checkboxes' the key and value returned by the form are the same. If you instead add a separate checkbox for each value you can define the key and value separately.

When I was modifying some form elements defined in the Domain Access module as 'checkboxes' and replaced them with several 'checkbox' elements I forgot to define the keys. I made sure to define the correct return values, since that's what should matter, but it didn't work. It became apparent that the Domain Access module actually wasn't checking the returned value - it was checking the key and it's not alone. It's often more convenient - but I had (although accidentally) reset my keys through a couple of array_merge() and they wasn't linked to domain id:s anymore.

That it's easy to reset an arrays index isn't the only reason to think twice about storing id:s in it. Another reason in at least Drupal is that individual form elements (which are array values) might be modified independently from it's key through eg. an #after_build. All such independent modifications becomes unable to modify the value in an array if the value used in eg. a submit handler is the key and not the value defined in eg. #return_value.

Using numeric keys in associative arrays should be used with caution - they are easily lost in the code and depending on them can result in some weird errors - especially if you're working with third party code. If you have to base your keys on numeric values they should be prefixed with a string (the # character is a prime candidate for this) to force PHP to always treat the array as associative. Also remember that storing necessary values as keys ties the array values to the array making it harder to alter them independently.