Here's my crack at it. If I understand your explanation aright, then by your example for 'moods' you want 'blue', 'skies' you want 'skies'; and for 'wibble' I'm going to use false (an empty array would have a different meaning - if, for example, 'hello' wasn't an acceptable key).
By "acceptable key" I mean one that is listed in the $_SESSION['id'] array. "Path" is a list of keys of nested subarrays (the path of $array['hello']['blue']['skies'] is array('hello','blue','skies'). If every element of a key's path is acceptable, then you want that key returned. If some elements of the key's path are not acceptable, then you want the last acceptable key in the path before the first unacceptable one. So if I removed 'blue' from the list of acceptable keys, then 'clear's path is array('hello', 'blue', 'skies') and you would want 'hello' returned from that.
I split it into two functions. One to do the recursive bit (building the path), and the other to find the "most acceptable" key in that path. (And then I wrote a third just to weld the other two together).
// Here we try to find the key. We return an array of the sub-, subsub-, subsubsubkeys
// within which the key is to be found, or false if the key isn't in the array at all.
function find_key($key, $array, $path=array())
{
if(array_key_exists($key, $array)) return $path; // Found the key on this path
$array = array_filter($array, 'is_array');
if(count($array)==0) return false; // No subarrays - the key is not on this path
foreach($array as $parent_key=>$subarray)
{
$found_path = find_key($key, $subarray, $path);
if($found_path===false) continue; // Didn't find the key in that subarray, try the next.
array_unshift($found_path,$parent_key); // Found the key in this subarray.
return $found_path;
}
return false; // Didn't find the key in any subarray.
}
// Here is where we decide whether to return the key itself, or the highest
// acceptable ancestor key.
function accept_key_or_parent($key, $path, $accepted_keys)
{
$path_length = count($path);
// Let's see how deep we can go and still remain acceptable.
for($i=0; $i<$path_length; $i++)
if(!in_array($path[$i], $accepted_keys))
break; // Don't accept unaccepted keys.
if($i<$path_length)
return array_pop($path); // Don't go down an unacceptable path
else
return $key; // Happy all the way down the line.
}
function key_or_parent($key, $array, $accepted_keys)
{
$path = find_key($key, $array);
if($path===false || count($path)==0) return false; // The key wasn't there at all.
return accept_key_or_parent($key, $path, $accepted_keys);
}
// Example
$should_be_clear = key_or_parent('clear', $array, array_keys($_SESSION['id']));
$should_be_sapphires = key_or_parent('big', $array, array_keys($_SESSION['id']));
$should_be_false = key_or_parent('foo', $array, array_keys($_SESSION['id']));