Search code examples
phpforeachtraversable

Can we use IterateAggregate or Iterator in foreach loop in php?


I am beginner to php and learning it from php.net. A note on that page(http://php.net/manual/en/class.traversable.php) says that:

Internal (built-in) classes that implement this interface can be used in a foreach construct and do not need to implement IteratorAggregate or Iterator.

What does this note says ? Does it means that we can use IteratorAggregate or Iterator inside foreach loop without any class or may be i am wrong. Can anyone tell what does this note says ??


Solution

  • The IteratorAggregate is an interface to create an external Iterator which allows you to traverse your custom class objects using foreach:

    class FooBarClass implements IteratorAggregate
    {
        public $property = "Nothing to see";
    
        public function __construct()
        {
        }
    
        public function getIterator()
        {
            return new ArrayIterator($this);
        }
    }
    
    $obj = new FooBar;
    
    foreach($obj as $key => $value) {
        print(var_dump($key, $value) . "\n");
    }
    

    The Iterator is also an interface for external iterators but allows your classes or objects to iterate themselves internally:

    class myIterator implements Iterator
    {
        private $position = 0;
        private $array = array('one', 'two', 'three');
    
        function rewind()
        {
            $this->position = 0;
        }
    
        function current()
        {
            return $this->array[$this->position];
        }
    
        function key()
        {
            return $this->position;
        }
    
        function next()
        {
            ++$this->position;
        }
    
        function valid()
        {
            return isset($this->array[$this->position]);
        }
    }
    

    Nonetheless, you can still traverse your objects in the same way as with IteratorAggregate.

    The difference between both is that IteratorAggregate is easier to implement than an Iterator and it is generally faster. The downside is it is just for traversal and does not provide next(), key(), etc methods, as these do not get called during the foreach traversal.

    While the Iterator (or more specific an OuterIterator or (easier) an IteratorIterator) allows you to have much finer control over the iteration through your object and to add custom exceptions on next(), key() or prev() failures, caching(), etc.

    The note you are referring to means that some of PHP's internal classes (written in C code) may implement this interface directly. Any userland class that needs to implement Traversable must do so by implement IteratorAggregate or Iterator or another descend from Traversable. See Pro PHP by Kevin McArthur p. 143f.

    The Traversable interface itself is an abstract base interface (with no methods as shown in the interface synopsis) that cannot be instantiated. However, it can be used to check whether a class is traversable using foreach or not.

    Traversable {
    }
    

    The confusion part is objects and arrays do not implement "Traversable", yet can be traversed by foreach, but you cannot check for foreach compatibility using an instanceof check or type hint.

    $myarray = array('one', 'two', 'three');
    $myobj = (object)$myarray;
    
    if ( !($myarray instanceof \Traversable) ) {
        print "myarray is NOT Traversable";
    }
    if ( !($myobj instanceof \Traversable) ) {
        print "myobj is NOT Traversable";
    }
    

    As mentioned before, every object can be traversed with foreach, but then you can just access public properties. Citing from the PHP manual on Object Iteration:

    PHP 5 provides a way for objects to be defined so it is possible to iterate through a list of items, with, for example a foreach statement. By default, all visible properties will be used for the iteration.

    So, if you encapsulate an object with private and protected values and write getter and setter methods to access it, you may like your class to implement IteratorAggregate or Iterator and write the logic to make these values accessible in foreach loops as needed.

    In short, objects that implement the Traversable interface (through Iterator or IteratorAggregate) "act like an array". However, it's not necessary for iterating the object. But you need to implement Iterator if you want to change their behavior. The same logic applies to built-in classes.