Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

PHP SPL ArrayIterator v simple foreach

Tags:

php

spl

Ok, just as the title says: what are the main benefits of using an ArrayIterator over a simple foreach loop.

I have an object which is being used as a container, its primary responsibility is storing an array of objects.

Someone suggested to me to make my class implement IteratorAggregate and return an ArrayIterator with my array of objects in: like this:

  public function getIterator()
  {  
    return new ArrayIterator($this->_objs);  
  }

I cant see any benefits to this as opposed to simply returning the array and then using a standard foreach to loop over them.

Please can someone explain why this would be a good idea?

like image 793
Marty Wallace Avatar asked May 28 '12 22:05

Marty Wallace


1 Answers

The benefits of implementing ArrayAccess is simple: You can access the whole object or just a very specific, even private property as array.

In this very simple example, we use Iterator and ArrayAccess to have access to a specific property named $array.

class Foo implements \ArrayAccess, \Iterator
{
    public $pointer = 0;

    private $array = [
        'first value',
        'second value',
        'third value',
    ];

    public function __construct()
    {
        $this->pointer = 0;
    }

    public function current()
    {
        return $this->array[ $this->pointer ];
    }

    public function key()
    {
        return $this->pointer;
    }

    public function next()
    {
        ++$this->pointer;
    }

    public function rewind()
    {
        $this->pointer = 0;
    }

    public function valid()
    {
        return isset( $this->array[ $this->position ] );
    }
}

We can now loop through it like we would loop through every array:

$foo = new Foo();
foreach ( $foo as $key => $value )
{
    print "Index: {$key} - Value: {$value}".PHP_EOL;
}

As seen in the previous example, we need to implement all that methods just to loop trough and have access to already present data. And to implement ArrayAccess, which allows us to actually set values, we'll have to implement another set of methods:

ArrayAccess {
    abstract public boolean offsetExists ( mixed $offset )
    abstract public mixed offsetGet ( mixed $offset )
    abstract public void offsetSet ( mixed $offset , mixed $value )
    abstract public void offsetUnset ( mixed $offset )
}

Now we can totally leave off writing and repeating that (current/key/next/etc) set of methods, by simply using IteratorAggregate. This sums it into a single, mandatory method: getIterator().

Now we'd have a resulting class like this:

class Bar implements \ArrayAccess, \IteratorAggregate
{
    private $array = [];

    public function getIterator()
    {
        return new ArrayIterator( $this->array );
    }
}

And we have full array access to the classes $array property. We can set and unset values, check if they return isset, can loop through them etc. Much more convenient. And it does exactly the same thing as all those eight methods above, just by implementing IteratorAggregate.

A last note: You'll likely do one more step and implement Traversable as well. This interface is very nice, as it allows you to make implementing either Iterator or IteratorAggregate mandatory without explicitly stating which one the implementing developer needs to use. It just explains that it is mandatory that the object has to be loopable.

$foo = new IteratorContainerController();
$foo->addDependency( Traversable $bar );

This means that the Dependency for IteratorContainerController() will always be either an Iterator or an IteratorAggregate implemented object. So someone can simply throw an IteratorAggregate in or, if s/he needs more fine grained control or does pre-processing of variables before they get set, s/he can simply use Iterator.

like image 142
kaiser Avatar answered Nov 10 '22 13:11

kaiser