95

I want to use a foreach loop with a variable, but this variable can be many different types, NULL for example.

So before foreach I test it:

if(is_array($var)){
  foreach($var as ...

But I realized that it can also be a class that implements Iterator interface. Maybe I am blind but how to check whether the class implements interface? Is there something like is_a function or inherits operator? I found class_implements, I can use it, but maybe there is something simpler?

And second, more important, I suppose this function exist, would be enough to check if the variable is_array or "implements Iterator interface" or should I test for something more?

PeeHaa
  • 71,436
  • 58
  • 190
  • 262
Voitcus
  • 4,463
  • 4
  • 24
  • 40

7 Answers7

88

If you are using foreach inside a function and you are expecting an array or a Traversable object you can type hint that function with:

function myFunction(array $a)
function myFunction(Traversable)

If you are not using foreach inside a function or you are expecting both you can simply use this construct to check if you can iterate over the variable:

if (is_array($a) or ($a instanceof Traversable))
Shoe
  • 74,840
  • 36
  • 166
  • 272
  • Thanks. I hope it is enough and there are/will be no other language constructs that can be iterated. – Voitcus Mar 24 '13 at 21:57
  • I had found `is_array` to be expensive. The computational cost seemed to increase with the size of array (which makes no sense since it's just checking if it's an array). But it happened to me shockingly in a library. See [my comment](http://stackoverflow.com/questions/358788/how-can-i-send-html-mails-with-included-css-with-phpmailer#comment68672222_2209829) in the linked question. Will `instanceof Traversable` work with arrays? I didn't get the chance to test its performance. – ADTC Dec 10 '16 at 04:23
  • @ADTC AFAIR an array is an instance of `Traversable` so yes. – Shoe Dec 10 '16 at 22:59
  • 1
    @Shoe I tried it [here](http://www.writephponline.com/). With `$var = array(1,2,3);` the results are: `is_array($var) = true` and `$var instanceof Traversable = false`. – ADTC Dec 11 '16 at 06:49
  • @ADTC Yeah, just checked. Arrays do not implement `Iterator` and therefore don't work with `Traversable`. – Shoe Dec 11 '16 at 12:02
15

foreach can handle arrays and objects. You can check this with:

$can_foreach = is_array($var) || is_object($var);
if ($can_foreach) {
    foreach ($var as ...
}

You don't need to specifically check for Traversable as others have hinted it in their answers, because all objects - like all arrays - are traversable in PHP.

More technically:

foreach works with all kinds of traversables, i.e. with arrays, with plain objects (where the accessible properties are traversed) and Traversable objects (or rather objects that define the internal get_iterator handler).

(source)

Simply said in common PHP programming, whenever a variable is

  • an array
  • an object

and is not

  • NULL
  • a resource
  • a scalar

you can use foreach on it.

Community
  • 1
  • 1
hakre
  • 193,403
  • 52
  • 435
  • 836
5

You can check instance of Traversable with a simple function. This would work for all this of Iterator because Iterator extends Traversable

function canLoop($mixed) {
    return is_array($mixed) || $mixed instanceof Traversable ? true : false;
}
Baba
  • 94,024
  • 28
  • 166
  • 217
  • 1
    "? true : false" part is redundant. instanceof already gives a bool value as a result. – Linas Sep 09 '20 at 10:37
2
<?php
$var = new ArrayIterator();

var_dump(is_array($var), ($var instanceof ArrayIterator));

returns bool(false) or bool(true)

Eduardo Cuomo
  • 17,828
  • 6
  • 117
  • 94
Alexey
  • 3,414
  • 7
  • 26
  • 44
2

PHP 7.1.0 has introduced the iterable pseudo-type and the is_iterable() function, which is specially designed for such a purpose:

This […] proposes a new iterable pseudo-type. This type is analogous to callable, accepting multiple types instead of one single type.

iterable accepts any array or object implementing Traversable. Both of these types are iterable using foreach and can be used with yield from within a generator.

function foo(iterable $iterable) {
    foreach ($iterable as $value) {
        // ...
    }
}

This […] also adds a function is_iterable() that returns a boolean: true if a value is iterable and will be accepted by the iterable pseudo-type, false for other values.

var_dump(is_iterable([1, 2, 3])); // bool(true)
var_dump(is_iterable(new ArrayIterator([1, 2, 3]))); // bool(true)
var_dump(is_iterable((function () { yield 1; })())); // bool(true)
var_dump(is_iterable(1)); // bool(false)
var_dump(is_iterable(new stdClass())); // bool(false)

You can also use the function is_array($var) to check if the passed variable is an array:

<?php
    var_dump( is_array(array()) ); // true
    var_dump( is_array(array(1, 2, 3)) ); // true
    var_dump( is_array($_SERVER) ); // true
?>

Read more in How to check if a variable is an array in PHP?

thomas
  • 785
  • 8
  • 7
0

Functions

<?php

/**
 * Is Array?
 * @param mixed $x
 * @return bool
 */
function isArray($x) : bool {
  return !isAssociative($x);
}

/**
 * Is Associative Array?
 * @param mixed $x
 * @return bool
 */
function isAssociative($x) : bool {
  if (!is_array($array)) {
    return false;
  }
  $i = count($array);
  while ($i > 0) {
    if (!isset($array[--$i])) {
      return true;
    }
  }
  return false;
}

Example

<?php

$arr = [ 'foo', 'bar' ];
$obj = [ 'foo' => 'bar' ];

var_dump(isAssociative($arr));
# bool(false)

var_dump(isAssociative($obj));
# bool(true)

var_dump(isArray($obj));
# bool(false)

var_dump(isArray($arr));
# bool(true)
Eduardo Cuomo
  • 17,828
  • 6
  • 117
  • 94
0

Since PHP 7.1 there is a pseudo-type iterable for exactly this purpose. Type-hinting iterable accepts any array as well as any implementation of the Traversable interface. PHP 7.1 also introduced the function is_iterable(). For older versions, see other answers here for accomplishing the equivalent type enforcement without the newer built-in features.

Fair play: As BlackHole pointed out, this question appears to be a duplicate of Iterable objects and array type hinting? and his or her answer goes into further detail than mine.

faintsignal
  • 1,828
  • 3
  • 22
  • 30