Dealing with deep arrays in PHP

Drupal code is heavily array-based, and it utilizes array structures that can be many levels deep. Here are a couple of complementary functions to query and manipulate such arrays:

<?php
/**
* Search for a key in an array, returning a path to the entry.
*
* @param $needle
*   A key to look for.
* @param $haystack
*   A keyed array.
* @param $forbidden
*   A list of keys to ignore.
* @param $path
*   The intermediate path. Internal use only.
* @return
*   The path to the parent of the first occurrence of the key, represented as an array where entries are consecutive keys.
*/
function array_key_path($needle, $haystack, $forbidden = array(), $path = array()) {
  foreach (
$haystack as $key => $val) {
    if (
in_array($key, $forbidden)) {
      continue;
    }
    if (
is_array($val) && is_array($sub = array_key_path($needle, $val, $forbidden, array_merge($path, (array)$key)))) {
      return
$sub;
    }
    elseif (
$key === $needle) {
      return
array_merge($path, (array)$key);
    }
  }
  return
FALSE;
}

/**
* Given a path, return a reference to the array entry.
*
* @param $array
*   A keyed array.
* @param $path
*   An array path, represented as an array where entries are consecutive keys.
* @return
*   A reference to the entry that corresponds to the given path.
*/
function &array_path(&$array, $path) {
 
$offset =& $array;
  if (
$path) foreach ($path as $index) {
   
$offset =& $offset[$index];
  }
  return
$offset;
}
?>

These functions are useful when the key you're looking for is not necessarily located in the array's root level. For example, a form field might be located a few levels deep due to fieldgroups. In that case, you would write something like:

<?php
// Given a $form, find an element and manipulate it.
$path = array_key_path('field_video', $form, array('#field_info'));
if (
$path !== FALSE) {
 
$field_video =& array_path($form, $path);
 
$field_video['#disabled'] = TRUE;
}
?>

Happy coding!

Comments

Awesome! Thanks:) Saved me a bit of time writing it ;)

An item usually contains both objects and arrays, so I don't think this code will work all the time. And always be careful when using these functions since it can make for a performance hit, and lazy coding.

Better to give a better example instead of directionless comment. You can post an example which works on objects and arrays and help the community more.

I used this lazy coding today and saved lot of headache.

Thanks for the caveat. It's true that no code will work all the time - use with caution!