Mike is currently working as a senior LAMP architect for a San Francisco start-! !up. With over 30-years of exeperience in the programming field, his only comment is "I should know better by now." Micheal is a DZone MVB and is not an employee of DZone and has posted 12 posts at DZone. You can read more from them at their website. View Full User Profile

Testing Arrays in PHP – Back to Basics…

12.29.2012
| 932 views |
  • submit to reddit

Sometimes, when you’re wallowing through your abstraction class layers, you find yourself using code for simple functions that are normally the focus of an Intro to Programming class.  The problem is that you spend so much time on advanced topics, that your common-knowledge of simpler functions becomes… murky… You have to stop what your’re doing and review the manual for simple functions just to convince yourself that the results you expect are the results you’re getting in your code.

Sound familiar?

I recently encountered just this issue while evaluation whether or not my data structures were populated by testing the structure against several PHP built-in functions designed to inform me of the status of the structure.  The data structure in-question was just a multi-dimensional array or arrays used to store non-relational records within a mongo collection.  Because the data itself is an array, and each element can also be an array, the code to support the structure is all recursive when it comes to structure traversal and validation.

So the testing of the elements within the array boiled down to a simple detection to determine if I was dealing with a data element (end-point) or another array of arrays.

PHP offers several built-in functions for this type of testing:

  • isset() – determine if a variable is set and not null
  • is_null() – determine if a variable is null
  • empty() – determine if a variable is empty: does not exist or if it’s value is FALSE

What I found myself doing was writing conditionals that used combinations of these three functions.  Reviewing my code later, when I asked myself if this was really necessary, I was somewhat embarrassed by the realization that I couldn’t conclusively say.  I mean, I know what these functions do, right?  I use them daily…  but I didn’t have 100%-confidence that I was using them correctly within the context that I needed.

Reviewing the documentation on the functions (I provided the PHP.net links above), I decided that I should study the problem further, write some test stubs, and see what PHP would tell me with the return values from these methods.  I thought the results were interesting enough to share, hence this article.

Using JSON notation, let’s create a simple data structure:

foo = ( { 
 { 'fname' : 'fred', 'lname' : 'flintstone', 'city' : 'bedrock' }, 
{ 'fname' : 'fred', 'lname' : 'flintstone', 'city' : 'bedrock' } 
} )

In PHP, this declaration would appear like so:

$bar = array('fname' => 'fred', 'lname' => 'flintstone', 'city' => 'bedrock'); 
$foo = array($bar, $bar);

If we var_dump() the contents of the array, along with their associated indexes, we get this output:

Array 
( 
    [0] => Array 
        ( 
            [fname] => fred 
            [lname] => flintstone 
            [city] => bedrock 
        ) 
    [1] => Array 
        ( 
            [fname] => fred 
            [lname] => flintstone 
            [city] => bedrock 
        ) 
) 
Complete Array (foo) with (bar) count: 2 
array[0] count: 3 
array[1] count: 3

So far, pretty simple.  Let’s pose these questions to see how the PHP built-in functions would handle the following scenarios:

  • If I unset() the fname in $foo[1], will the count of the $foo[1] change?
  • If I unset() all of the $foo[1]‘s indexes (lname, city), what will the count of $foo[1] be?
  • What does $foo look like now?
    • Does $foo[1] test true for isset()?
    • Does $foo[1] test true for is_null()?
    • Does $foo[1] test true for empty()?
  • What does $foo look like if I unset $foo[1]?
    • Does $foo[1] test true for isset()?
    • Does $foo[1] test true for is_null()?
    • Does $foo[1] test true for empty()?
  • What is the count() of $foo?
  • If I set $foo[1] to null, do my results change?
    • Does $foo[1] test true for isset()?
    • Does $foo[1] test true for is_null()?
    • Does $foo[1] test true for empty()?
  • Does setting $foo[1] to null count()?

I wrote a small test-stub to answer all of these questions (listing is at the end of this article) and the results I got back were interesting.  Let’s take a look by answering all of the questions posed above:

The array count of $bar, as expected, is: 2.  Additionally, each sub-array within $bar has a count of: 3.  No surprises.

After I unset($foo[1]['fname']), the count of $foo[1] drops to: 2.  Not surprised.

After I unset the remaining indexed values in $foo[1] (lname, city), the count of $foo[1] is now: 0.  Not surprised.

The count($foo) however is still: 2.  Even though all of the indexed elements in $foo[1] were unset, the placeholder for $foo[1] still exists and counts.  This told me that, in a scenario where you may be stripping invalid elements from an input array, in the border-case where all of the elements are removed, using count() on the parent (containing) array will not indicate this.  Interesting.

$foo[1] tests TRUE for isset() — not surprising given the above.

$foo[1] tests TRUE for is_null() — not surprising considering the container index is basically pointing to nothing (zero elements).

$foo[1] tests TRUE for empty() — as expected.

Next, I remove the $foo[1] from the $foo array using the unset() function.  The var_dump() of $foo now shows only our first element which is still untouched as the only member within the array.  The count() of $foo is now: 1.

We apply the same series of tests and get:

$foo[1] tests FALSE for isset() and, bonus, generates a PHP notice because the offset index no longer exists.  Interesting.

$foo[1] tests TRUE for is_null().  Not surprised.

$foo[1] tests TRUE for empty().  Again, not surprised.

Finally, we’re going to set $foo[1] to NULL under the assumption that we don’t want to lose the index position of the data set during processing providing a means for quickly detecting which elements in the input data were completely discarded.

$foo[1] is now set to NULL with the following results:

$foo[1] tests FALSE for isset().  Sort of interesting in that we have a placeholder which can be counted, but since it is NULL value, it’s not “set” or present.

$foo[1] tests TRUE for is_null().  Expected.

$foo[1] tests TRUE for empty().  As documented.

Interesting, the count($foo) is back to: 2, even though the $foo[1] index is null, and no data, and tests FALSE for isset().

These results caused the generation of the following matrix:

array_stateChecked-boxes indicate a YES or TRUE answer while unchecked-boxes are NO or FALSE.

Of the functions tested, empty() appears to provide the best across-the-board response to decision testing with code for determining the presence (or absence) of array content as a stand-alone test.

Which leaves the question that started this entire trip down the rabbit-hole:  do I need to use multiple functions to test the state of an array or sub-array?

Probably not, in most cases – I can shave a few ticks here and  there from my code by reducing the complexity of my decision statements by eliminating redundant functions as long as I keep tight controls over my expectations of the data itself, the structure being used, and the state of the indexes.

This code was tested on an Ubuntu 12.10 install using PHP 5.4.6 (cli).  I hope you found this experiment as interesting as I did; it’s good to go back and review the basics so you KNOW what’s going in in your code…

Happy Coding!

Program Listing:

<?php 
$bar = array('fname' => 'fred', 'lname' => 'flintstone', 'city' => 'bedrock'); 
$foo = array($bar, $bar); 
print_r($foo); 
echo 'Complete Array (foo) with (bar) count: ' . count($foo) . PHP_EOL; 
for ($index = 0; $index < count($foo); $index++) { 
    echo 'array[' . $index . '] count: ' . count($foo[$index]) . PHP_EOL; 
} 
echo 'Nuking index 1 of foo...' . PHP_EOL; 
unset($foo[1]['fname']); 
echo 'unset fname...count of array[1] is: ' . count($foo[1]) . PHP_EOL; 
unset($foo[1]['lname']); 
unset($foo[1]['city']); 
echo 'all members unset... count: ' . count($foo[1]) . PHP_EOL; 
print_r($foo); 
if (isset($foo[1])) { 
    echo 'foo[1] tests true for isset()' . PHP_EOL; 
} else { 
    echo 'foo[1] tests false for isset()' . PHP_EOL; 
} 
if (is_null($foo[1])) { 
    echo 'foo[1] tests true as null' . PHP_EOL; 
} else { 
    echo 'foo[1] tests false as null' . PHP_EOL; 
} 
if (empty($foo[1])) { 
    echo 'foo[1] tests true for empty' . PHP_EOL; 
} else { 
    echo 'foo[1] tests false for empty' . PHP_EOL; 
} 
echo 'unset foo[1]...' . PHP_EOL; 
unset($foo[1]); 
print_r($foo); 
if (isset($foo[1])) { 
    echo 'foo[1] tests true for isset()' . PHP_EOL; 
} else { 
    echo 'foo[1] tests false for isset()' . PHP_EOL; 
} 
echo 'is_null($foo[1]) generates a PHP Notice:' . PHP_EOL; 
if (is_null($foo[1])) { 
    echo 'foo[1] tests true as null' . PHP_EOL; 
} else { 
    echo 'foo[1] tests false as null' . PHP_EOL; 
} 
if (empty($foo[1])) { 
    echo 'foo[1] tests true for empty' . PHP_EOL; 
} else { 
    echo 'foo[1] tests false for empty' . PHP_EOL; 
} 
echo 'count of foo: ' . count($foo). PHP_EOL; 
echo 'setting foo[1] to null...' . PHP_EOL; 
$foo[1] = null; 
print_r($foo); 
if (isset($foo[1])) { 
    echo 'foo[1] tests true for isset()' . PHP_EOL; 
} else { 
    echo 'foo[1] tests false for isset()' . PHP_EOL; 
} 
if (is_null($foo[1])) { 
    echo 'foo[1] tests true as null' . PHP_EOL; 
} else { 
    echo 'foo[1] tests false as null' . PHP_EOL; 
} 
if (empty($foo[1])) { 
    echo 'foo[1] tests true for empty' . PHP_EOL; 
} else { 
    echo 'foo[1] tests false for empty' . PHP_EOL; 
} 
echo 'does null count?' . PHP_EOL; 
echo 'count of foo: ' . count($foo). PHP_EOL; 

									

Program Output:

Array 
( 
    [0] => Array 
        ( 
            [fname] => fred 
            [lname] => flintstone 
            [city] => bedrock 
        ) 
    [1] => Array 
        ( 
            [fname] => fred 
            [lname] => flintstone 
            [city] => bedrock 
        ) 
) 
Complete Array (foo) with (bar) count: 2 
array[0] count: 3 
array[1] count: 3 
Nuking index 1 of foo... 
unset fname...count of array[1] is: 2 
all members unset... count: 0 
Array 
( 
    [0] => Array 
        ( 
            [fname] => fred 
            [lname] => flintstone 
            [city] => bedrock 
        ) 
    [1] => Array 
        ( 
        ) 
) 
foo[1] tests true for isset() 
foo[1] tests false as null 
foo[1] tests true for empty 
unset foo[1]... 
Array 
( 
    [0] => Array 
        ( 
            [fname] => fred 
            [lname] => flintstone 
            [city] => bedrock 
        ) 
) 
foo[1] tests false for isset() 
is_null($foo[1]) generates a PHP Notice: 
PHP Notice:  Undefined offset: 1 in /home/Namaste/utilities/unsetTest.php on line 41 
foo[1] tests true as null 
foo[1] tests true for empty 
count of foo: 1 
setting foo[1] to null... 
Array 
( 
    [0] => Array 
        ( 
            [fname] => fred 
            [lname] => flintstone 
            [city] => bedrock 
        ) 
    [1] => 
) 
foo[1] tests false for isset() 
foo[1] tests true as null 
foo[1] tests true for empty 
does null count? 
count of foo: 2 

									


 

Published at DZone with permission of Micheal Shallop, author and DZone MVB. (source)

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Tags: