Recursive isset or array_key_exists

I have an array like this:

$config = array(
	'KEY-A' => array(
		'KEY-B' => array(
			'KEY-C' => 'A test value',
			'KEY-D' => 'Another test value'
		)
	),
	'KEY-E' => array(
		'KEY-F' => 'Something'
	)
);

I want to create a function that will test whether or not a key exists by passing in an array like this:

function exists($args) {
	# determine if key exists
}

exists(array('KEY-A', 'KEY-B', 'KEY-C'));

I can do it like this, but I am concerned about the performance of eval:

return eval('return isset($config[\\'' . implode('\\'][\\'', $args) . '\\']);');

I am trying to work on a kind of recursive isset or array_key_exists function as described in the php documentation but am having a hard time. Does anyone see an efficient way to accomplish this?

so what is going to be returned? Array of true or false values?

Just a single boolean

so you are passing the list of keys you want to search for; what about the array you are trying to search in? Is it local to the function or also passed?

The array I’m searching as well as the function that does the searching will both be members of the same class. Sorry, that was lost in translation when I tried to simplify the example.

http://php.net/array_key_exists

And then I searched for “recursive” and the first result:

Probably could be better…


function exists ()
{
    $args = func_get_args();
    $arr  = array_shift( $args );

    foreach ( array_keys( $arr ) as $key ) {
        if ( in_array( $key, $args ) ) return true;
    }

    foreach ( $arr as $value ) {
        if ( is_array( $value ) ) {
            $f = call_user_func_array( __function__, array_merge( array( $value ), $args ) );
            if ( $f === true ) return true;
        }
    }

    return false;
}

$config = array(
    'KEY-A' => array(
        'KEY-B' => array(
            'KEY-C' => 'A test value',
            'KEY-D' => 'Another test value'
        )
    ),
    'KEY-E' => array(
        'KEY-F' => 'Something'
    )
);

var_dump( exists( $config, 'KEY-C', 'KEY-F' ) );

Yes, I did go to the php docs for array_key_exists and search for recursive as I mentioned in my post however I was having trouble modifying that function to suit my needs.

logic_earth, thank you. Your variation works nicely.

You just loop through the array of args and call array_key_exists_r function and combine the results with ‘and’ operator.

To bhanson: good find! I was about to write it myself as an exercise but this is very close to what I had in mind and I am a big fan of a recursive stuff.

Actually, forgive me, I spoke too soon. logic_earth’s example returns true however it should return false because

$config['KEY-C']['KEY-F']

does not exist. I am trying to design a function that would return true for

exists($config, 'KEY-A', 'KEY-B', 'KEY-C')

but false for something like

exists($config, 'KEY-F')

Now you are confusing us… Key-F is a valid key…

Yes but if I go

$x = $config['KEY-F']

that won’t work. It’s not just that a key exists anywhere it’s that it exists in the order that the args are passed in.

$x = $config['KEY-E']['KEY-F']

would work so

exists($config, 'KEY-E', 'KEY-F')

should return true

That’s the different ball game… If you want us to help you, you need to be very precise in your reqs otherwise it is a waste of time…

Off the top of my head (untested, may not work at all or may be highly inefficient):


function exists($scope)
{
	$keys = func_get_args();
	array_shift($keys);
	
	if (empty($keys))
		return false;
	
	foreach ($keys as $key)
	{
		if ( ! isset($scope[$key]))
			return false;
		
		$scope = $scope[$key];
	}
	
	return true;
}

Slightly modified version of Salathe’s function works, thanks you! Now I have two options, I just need to determine which one is most efficient. So far they neither one has a real effect on script execution time.

function exists1() {
	$args = func_get_args();
	$arr = array_shift($args);

	return eval('return isset($arr[\\'' . implode('\\'][\\'', $args) . '\\']);');
}

function exists2() {
	$args = func_get_args();
	$arr = array_shift($args);

	if(empty($args)) {
		return false;
	}

	foreach($keys as $key) {
		if(! isset($arr[$key])) {
			return false;
		}

		$arr = $arr[$key];
	}

	return true;
}

function array_structure_exists($keys, $array) {
    if (!count($array) || !count($keys)) {
        return false;
    }
    foreach ($keys as $key) {
        if (!is_array($array) || !is_scalar($key) || !array_key_exists($key, $array)) {
            return false;
        } else {
            $array = $array[$key];
        }
    }
    return true;
}

It’s important to test that you actually have an array before trying to see if an indice exists in it. If it was a string, you will not get the expected nor desired result.

For example


$config = array(
    'KEY-A' => 'bbb'
);
$keys = array('KEY-A', 'KEY-B');

You would be trying to test if a string has an array indice of ‘KEY-B’, which is impossible. However, the result will sometimes be true if using isset()…php will try to convert the string into a number, because it allows you to access individual characters in string via numeric index. It uses its typical behavior for typecasting a string into an int.

To crmalibu: check this out… your function will not work with the following arguments:


$array = array(
	'KEY-A' => array(
		'KEY-B' => array(
			'KEY-C' => 'A test value',
			'KEY-D' => 'Another test value',
		)
	),
	'KEY-E' => array(
		'KEY-F' => 'Something'
	)
);

$keys = array('KEY-A','KEY-E','KEY-F');

We should get true and I am getting false.

To BeefaroniPoni: in exists2 function where is $keys defined?

forkaya,

$array[‘KEY-A’][‘KEY-E’][‘KEY-F’] doesn’t exist.

BeefaroniPoni said:

It’s not just that a key exists anywhere it’s that it exists in the order that the args are passed in.

There is no mentioning about keys being in array… or I am off completely.