In order to explain this, I'll have to talk about three interfaces: Iterator, IteratorAggregate, and Traversable
.
Let's explain in parts:
According to the PHP Manual (translated by me, more or less so):
Traversable: Interface to detect if a class can be iterated with foreach
.
Now this information is important.
Interface abstract base that can not be instantiated. Instead, you should use IteratorAggregate
or Iterator
.
You need to understand a point: Traversable
is to detect that a class is iterable via foreach
. It is a special interface, used internally by PHP.
PHP asks you to use the
IteratorAggregate
or
Iterator
interfaces because these two interfaces extend the
Traversable
interface (for those who do not know, in php it is possible for one interface to inherit the other).
And so, php uses Traversable
to know if the class can be iterated with foreach
, but to set the behavior you should use Iterator
or IteratorAggregate
.
But why two interfaces, instead of one?
There is a change in the form of implementation. With Iterator
you need to implement 5 methods in your class should be iterated, that is, this is how you will behave in foreach
.
In the case of IteratorAggregate
you only need to implement a method, getIterator
, which should return another object that implements Iterator, so you can iterate over items in your class.
If the two other interfaces already exist, then what is Traversable for?
Directly, we can immediately remind you that PHP 5 accepts you to define the type of argument that a function or method should receive. This is called Type Induction.
For example, if I want an argument to be of a given class, I should define this in the function / method parameter.
Example:
function iter(MinhaClasse $objeto) {
}
But in addition to inducing the class itself that will be accepted in the passage of the argument, this typing also allows you to pass a class that is the parent class, or the interface. So, in addition to PHP checking if that class is passed, it can also check if a particular class implements an interface or is a child of another class.
class X {}
class Y extends X{}
class Z{}
function eh_valido(X $x) {}
eh_valido(new X); // SIM
eh_valido(new Y); // SIM
eh_valido(new Z); // Não
So, if I have a class that implements Iterator
and another that implements IteratorAggregate
, how would I accept the two as a parameter of a function / method, since both define a behavior of the class in relation to iteration? Yes, just use Traversable
as type induction.
Taking as an example, the ArrayIterator
class in PHP implements Iterator
. The ArrayObject
class implements IteratorAggregate
. Both are iterated with foreach
, since the implementations of the two inherit Traversable
.
Then, see the following tests:
$obj = new ArrayObject;
$it = new ArrayIterator($obj);
var_dump($obj instanceof IteratorAggregate); // bool(true)
var_dump($obj instanceof Iterator); // bool(false)
var_dump($obj instanceof Traversable); // bool(true)
var_dump($it instanceof Iterator); // bool(true)
var_dump($it instanceof IteratorAggregate); // bool(false)
var_dump($it instanceof Traversable); // bool(true)
So we can do this:
function itera_isso_pra_mim(Traversable $iterator)
{
return get_class($iterator);
}
itera_isso_pra_mim(new ArrayObject); // string(ArrayObject)
itera_isso_pra_mim(new ArrayIterator); // string(ArrayIterator)
itera_isso_pra_mim(new stdClass); // Gera um erro, pois não implementa nada que herde Traversable
All this explanation I made is of paramount importance when you see a function of spl iterator, you understand why the definition of the parameter is Traversable
.
See an example with the iterator_to_array
function (the Portuguese manual is wrong, you have to look at the English original).
Function Skeleton:
iterator_to_array ( Traversable $iterator [, bool $use_keys = true ] )
As an example, you can use as an argument of iterator_to_array
the following classes: DatePeriod
, ArrayIterator
, ArrayObject
, SplStack
, CallbackFilterIterator
among others. All of these indirectly implement Traversable
.