** 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:
1
2
3
4
5
| <!--?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:
1
2
3
4
5
6
| <!--?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
1
| 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:
1
2
3
4
5
6
7
8
9
10
11
| <!--?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.
0 comments:
Post a Comment