PHP isset() and multi-dimentional array

** The issue discussed in this post has been fixed since PHP5.4.0, so the below discussion and solution are for PHP 5.3.x or lower. Thanks David for clarifying.

A few weeks ago I covered how to check the existence of an array element in PHP. In the post I explained why isset() is dangerous to check the existence of elements in an array. I also proposed a better solution (the isset()+array_key_exists() method) to do the checking.

Today I’m going to discuss another strange (and dangerous) behavior brought along with isset() function and multi-dimensional arrays.

The problem

Let’s consider this simple code:

<!--?php $a = array('test'=-->'ABC');
var_dump(isset($a['test']));                       //true
var_dump(isset($a['test']['non_exist']));          //true?!!
var_dump(isset($a['test']['non_exist']) || array_key_exists('non_exist', $a['test'])); //true again?!!!
?>

Surprise, huh? Isset() returns true for a non-exist element!

What even worse is that the previous proposed method (the isset()+ array_key_exists() method) also gives a wrong result! This is because isset() returns true for the non_exist element so the overall OR operation will become “true”. The array_key_exists() is never implemented.

The reason

So why isset() returns true for a non-exist element? I’m not sure the exact reason but I have a guess:

PHP first look at $a[‘test’]. Since $a[‘test’] does exist, isset($a[‘test’]) returns true. Then PHP checks the 2nd dimension: the ‘non_exist’ element. As $a[‘test’] is a string, it is also considered as an array (In PHP, string is a sequential array by type-casting). When checking the sequential array where all index should be integers, the index [‘non_exist’] is **converted** to an integer which equals zero. So actually PHP is checking isset($a[‘test’][0]). Unfortunately $a[‘test’][0] does really exists (with value ‘A’). So the overall result of this checking is “true”.

To verify this guess, let’s run this code:

<!--?php $a = array(1=-->'', 2=>'ABC');
var_dump(isset($a[1])); //true
var_dump(isset($a[1]['t'])); //false => $a[1] is empty string, $a[1][0] doesn't exist
var_dump(isset($a[2])); //true
var_dump(isset($a[2]['t'])); //true => $a[2] is 'abc', so $a[2][0] exists and equals 'A'.
?>

The result has shown that my guess is pretty reasonable.

The solution

You say: OK, I know your guess is somehow right, so how to fix it?

Usually when we check the existence of elements in multiple dimensional array, we use  something like

array_key_exists('non_exist', $a['test']); 

Yes. This is true…but if you really do so in our case, you will get this warning:

Warning: array_key_exists() expects parameter 2 to be array, string given 

Somehow for unknown reason array_key_exists() doesn’t consider string as array now and is complaining us.

So what’s the solution?

Complete array element existence checking function

Combined with what I proposed in the previous and this post, I have worked out a function that checks whether an element does exist in an array, regardless the array’s dimensions:

<!--?php function elementExists($key, $array){     if (is_array($key)) {         $curArray = $array; 		$lastKey = array_pop($key); 		foreach($key as $oneKey) { 			if (!elementExists($oneKey, $curArray)) return false; 			$curArray = $curArray[$oneKey]; 		} 		return is_array($curArray) && elementExists($lastKey, $curArray); 	} else { 		return isset($array[$key]) || array_key_exists($key, $array); 	} } $a=array(1,2,3,4, 'dim1'=-->array('dim2'=>array('dim3'=>null)));

//multi-dimension : check if $a['dim1']['dim2']['dim3']['dim4'] exists:
var_dump(elementExists(array('dim1', 'dim2', 'dim3', 'dim4'), $a)); //false

//multi-dimension : check if $a['dim1']['dim2']['dim3'] exists:
var_dump(elementExists(array('dim1', 'dim2', 'dim3'), $a)); //true

//single dimension : check if $a['dim1'] exists:
var_dump(elementExists('dim1', $a)); //true
?>

This piece of codes looks quite awful and dirty, and its performance  is not evaluated. I think there are more elegant (and faster) codes to do the same thing. Since I’m in a hurry and got to complete my project ASAP, I prefer to leave it as it is now.

Comments are always welcomed!

10 Replies to “PHP isset() and multi-dimentional array”

    1. In the 3rd line of your code, you should write “return true;”, otherwise array_key_exists() will always be called no matter isset() is true or false.

      BTW, you could combine all those lines into just one line code:

      function array_isset($key, Array $array) {
      return isset($array[$key]) || array_key_exists($key, $array);
      }

      This will gives you the same result.

      1. Thanks, somehow missed the return.

        Now timings are closer, like they should:
        Time array_key_exists(‘a’, $array): 0.68053483963013 sec
        Time array_key_exists(‘b’, $array): 0.68176889419556 sec
        Time array_isset(‘a’, $array): 0.7919340133667 sec
        Time array_isset(‘b’, $array): 1.2284920215607 sec

        Timings for your one-liner are equal to my code (with return).
        But i still see that the wrapper function needs at least 1/10 extra time. So i would rather simly stick to array_key_exists() than a own wrapper function.

        FYI:
        I would not use “isset() || array_key_exists()” because that code assumes about a left to right order of execution. Many languages do not garantee such a order. So i try to write code that does garantuee a order. Does PHP garantuee something like that?

        1. Your result looks interesting. Would you mind posting your testing code here so that we can dig into it?

          I wonder if ‘a’ and ‘b’ are non-existing keys in the $array. For detecting an non-existing key, you better use array_key_exists() alone. isset() is a short-cut to check if the key exists. If isset() is true, then we can ensure the element exists and no need to do the array_key_exists(). However, if the element exist in the array and is set null (i.e. $array[‘a’] = NULL), isset() will tell you that it doesn’t exist. That’s why we need to use array_key_exists() to double confirm. Array_key_exists() will always give you the “correct” answer but it is slow so we want to use it only when it is necessary.

          For an non-existing key, the wrapper function will actually go through two tests : isset() and array_key_exists(), so it definitely slower than just array_key_exists(). If the most of the keys that you are checking are non-existing, then there is no doubt to use array_key_exists() directly.

          I have another post discussing the use and benchmarking of isset()+array_key_exists() method. It may interest you.

          Yes, for boolean operators, PHP is doing the left-to-right order. See here: http://www.php.net/manual/en/language.operators.precedence.php

        2. Finally I got your test code in github.

          The reason why the combined method is 2x slower is explained here:

          In the testing code, the combined checking is wrapped into a function array_isset(). Every time when PHP makes a function call it adds some overheads. So the measured time is actually includes those overheads. If you change your code (in the while-loop) :

          if(array_isset(‘a’, $array))

          into

          if (isset($array[‘a’]) || array_key_exists(‘a’, $array))

          The result will be more reasonable. Here is the benchmarking result with your code with the that modification:

          isset($array[‘a’]): true – should be true
          isset($array[‘b’]): false – should be true
          array_key_exists(‘a’, $array[‘a’]): true – should be true
          array_key_exists(‘b’, $array[‘b’]): true – should be true
          Time isset($array[‘a’]): 0.23629188537598 sec (t1)
          Time isset($array[‘b’]): 0.18283319473267 sec (t2)
          Time array_key_exists(‘a’, $array): 0.80693697929382 sec (t3)
          Time array_key_exists(‘b’, $array): 0.80788898468018 sec (t4)
          — isset() should be faster by factor x3
          Time array_isset(‘a’, $array): 0.2564389705658 sec (t5)
          Time array_isset(‘b’, $array): 0.97402620315552 sec (t6)
          — array_isset() is much slower than array_key_exists. Damn.

          Note that (t5) is much faster than (t3), and (t6) ~= (t2) + (t4) which is reasonable because it does both isset() and array_key_exists() checking.

  1. <?php

    $f= $_POST["clientno"];
    $a= $_POST["name"];
    $b= $_POST["address"];
    $c= $_POST["city"];
    $d= $_POST["pincode"];
    $e= $_POST["state"];

    $conn_mysql=mysql_connect("localhost","root","");
    if($conn_mysql)
    {
    echo "Connction Created “;
    }
    else
    {
    echo “Not Created “;
    }
    $mysql_db=mysql_select_db(“client”);
    if(!$mysql_db)
    {
    echo “DB Not Selected “;
    }
    else
    {
    echo “DB is Selected “;
    }

    $sqlinsert=”INSERT into client_mstr(clientno,name,address,city,pincode,state)values(‘$f’,’$a’,’$b’,’$c’,’$d’,’$e’)”;
    $result=mysql_query(“$sqlinsert”);
    if($result)
    {
    echo “Table Values are Inserted “;
    }
    else
    {
    echo “Data Are Not Inserted”;
    }
    ?>

    Solving My Error Or Reply Me
    Undefined Index : clientno
    Undefined Index : name
    Undefined Index : address
    Undefined Index : city
    Undefined Index : pincode
    Undefined Index : state

Leave a Reply

Your email address will not be published. Required fields are marked *