20

I have a relatively simple function which uses a foreach

function foo($t) {
     $result;
     foreach($t as $val) {
         $result = dosomething($result, $val);
     }
     return $result;
}

I would like to type hint, and Traversable seems to be the exact type hint I need

 function foo(Traversable $t) {

However this gives a E_RECOVERABLE_ERROR when using an array (which is of course usable in a foreach): example

 Argument 1 passed to foo() must implement interface Traversable, array given

Is there a way to type hint or is this not possible?

dave
  • 11,641
  • 5
  • 47
  • 65
dtech
  • 13,741
  • 11
  • 48
  • 73
  • From the manual (the link that you posted): `Abstract base interface that cannot be implemented alone. Instead it must be implemented by either IteratorAggregate or Iterator.` – Nir Alfasi Jun 10 '13 at 19:55
  • I believe you can actually use `array` as the hint, I will test my theory, yes you can – Dale Jun 10 '13 at 19:56
  • @alfasin And as such any class implementing `Iterator` will also match `Traversable`, as `Iterator` is a subtype of `Traversable`. Basic OOP-mechanics – dtech Jun 10 '13 at 19:58
  • Please consider solution provided to the [comment in manual](http://pl1.php.net/manual/en/class.traversable.php#99195). BTW, [I asked a similar question](http://stackoverflow.com/q/15603952/2088851). – Voitcus Jun 10 '13 at 19:59
  • 1
    @Dale I apologize in advance as I must be missing something: Array don't implement Iterator (AFAIK), and you're trying to use Array, no ? – Nir Alfasi Jun 10 '13 at 20:05
  • @alfasin I was just mentioning you can use `array` as the type hint `function foo(array $array){}` – Dale Jun 10 '13 at 20:07
  • @Dale but array doesn't implement `Iterator` nor `Traversable` – Nir Alfasi Jun 10 '13 at 20:08
  • @alfasin I didn't say that it does. I'm suggesting he uses array as the type hint, good bye. – Dale Jun 10 '13 at 20:09
  • Sorry - my last to comments were intended for @dtech not Dale... – Nir Alfasi Jun 10 '13 at 20:14
  • Possible duplicate of [Iterable objects and array type hinting?](http://stackoverflow.com/questions/3584700/iterable-objects-and-array-type-hinting) – Blackhole Sep 04 '16 at 19:09

4 Answers4

25

PHP 7.1 introduces the iterable type declaration for this purpose, which accepts both arrays and instances of \Traversable.

In previous versions, you'll have to omit the type declaration.

apaderno
  • 28,547
  • 16
  • 75
  • 90
Andrea
  • 19,134
  • 4
  • 43
  • 65
8

There is a bug about this: #41942. Closed as 'not a bug'. As PHP arrays are not objects they cannot implement an interface and a such there is no way to type hint both array and Traversable.

You can use iterator_to_array, ArrayIterator or omit the type hint. Note that iterator_to_array will copy the whole iterator into an array an might thus be inefficient.

// These functions are functionally equivalent but do not all accept the same arguments
function foo(array $a) { foobar($a); }
function bar(Traversable $a) { foobar($a); }
function foobar($a) {
    foreach($a as $key => $value) {
    }
}

$array = array(1,2,3)
$traversable = new MyTraversableObject();

foo($array);
foo(iterator_to_array($traversable));

bar(new ArrayIterator($array));
bar($traversable);

foobar($array);
foobar($traversable);
dtech
  • 13,741
  • 11
  • 48
  • 73
4

Same problem. I've given up I simply manually code everything in the function.

This should give you the functionality you want:

function MyFunction($traversable)
{
    if(!$traversable instanceof Traversable && !is_array($traversable))
    {
        throw new InvalidArgumentException(sprintf(
            'Myfunction($traversable = %s): Invalid argument $traversable.'
            ,var_export($traversable, true)
       ));
    }
}

EDIT

If you only want to display type of $traversable. And if you want the functionality inheritable in child classes.

public function MyMethod($traversable)
{
    if(!$traversable instanceof Traversable && !is_array($traversable))
    {
        throw new InvalidArgumentException(sprintf(
            '%s::MyMethod($traversable): Invalid argument $traversable of type `%s`.'
            ,get_class($this)
            ,gettype($traversable)
       ));
    }
}
mAsT3RpEE
  • 1,818
  • 1
  • 17
  • 14
2

The problem is, that arrays are no objects, so they can't implement an interface. So you can't type hint both, array and Traversable.

bpoiss
  • 13,673
  • 3
  • 35
  • 49
  • 1
    Are there other PHP native entities which are usable in `foreach` but do not implement `Traversable`? – dtech Jun 10 '13 at 19:59
  • No, but you can write your own class that implements `IteratorAggregate` or `Iterator`, and they extend `Traversable` – bpoiss Jun 10 '13 at 20:14
  • 1
    But there it is: all objects iterable in `foreach`. If the object implements `Traversable` the iterator (or the aggregated iterator) will be traversed, else the object's public properties http://php.net/manual/en/control-structures.foreach.php – pozs Jun 10 '13 at 20:18