tags:

views:

102

answers:

5

example:

foreach($boxes as $box) {
    echo "$box \n";
}

Used to be fairly easy, I could just wrap the foreach around a check like:

if(is_array($boxes) && count($boxes) > 0) {
    //foreach loop here
}

Without having to worry about a warning getting thrown if for whatever reason bad input was passed to the $boxes array.

When Iterators, were added to the mix, this no longer works, as Iteratable objects are not arrays. So, I have a few solutions, but am wondering if there is a 'best practice' for this.

// 1:
if($boxes instanceof Traversable && count($boxes) > 0) {
    //foreach loop here
}

// 2:
if($boxes && count($boxes) > 0) {
    //foreach loops here
}

There are others, but these seem like the most obvious. Anyone have any suggestions. PHP docs seem to be silent.

+4  A: 

You shouldn't have the count($array) > 0 part, because a) foreach works fine with empty arrays, b) objects can be Traversable yet not be Countable and c) the value returned by count() may even (for objects) be disconnected from the number of items the traversal will yield.

And #1 there is different from #2; since $boxes instanceOf Traversable is not the same as $boxes. Also note that internally arrays don't implement Traversable.

I would go with

if (is_array($boxes) || $boxes instanceof Traversable) {
    foreach (...)
}

This still doesn't guarantee that the traversal will be successful; the iteration may throw an exception at any point. In particular, for some classes it may not make sense to traverse them more than once.

NullUserException
Ah, yeah... I like this. Seems like I was complicating things a bit. I'm going to do some tests with this setup and see how it works out.
gabe.
+2  A: 

I think generally in these cases you would probably know that the variable is going to be iterable if it is not null or false etc., so I would be happy to just do:

if ($boxes) {
    foreach ($boxes as $box) {}
}

Maybe that is naive though

Tom Haigh
That should be fine, but note that if $boxes was defined as a number or a string it would still evaluate to true.
NullUserException
This solution doesn't seem complete enough. Any object that is not null/false will still be evaluated by the foreach, even if it is not an array or iterable. OP might not have the luxury of making these assumptions.
stjowa
In general, yes. But I'm specifically looking to eliminate Notices in our code where for whatever reason $boxes has something in it, but the foreach raises a Notice. Likely there are other issues further up the chain that need to be fixed, but for now, cleaning up the logs enough to find those errors is my priority. Thanks!
gabe.
A: 

One possibility depending on your php version is a cast:

<?php

$a = array('foo', array('bar'));

foreach ($a as $thing)
  foreach ((array) $thing as $item) // <-- here
    echo "$item\n";
?>
Robin
A: 

This test will give true for both arrays and array-like objects

if (is_array($obj) || $obj instanceof Traversable) {
    foreach ($obj as $item) { /* foreach loop is safe here */
    }
}
jmz
A: 

In PHP5 you can iterate over any array or object so..

if (is_array($my_var) || is_object($my_var)) {
    // Do some foreachin'
}
Triptych