The Iterator Interface

The Iterator Interface

PHP5's built-in abstract interface Iterator defines five methods that allow foreach to understand how to interact with a non-array parameter. Here's the interface definition:

   <?php    /**     * Traversable is an empty interface     */    interface Traversable {}    interface Iterator implements Traversable {      /**       * Rewind the Iterator to the first element.       */      function rewind();      /**       * Return the current element.       */      function current();      /**       * Return the key of the current element.       */      function key();      /**       * Move forward to next element.       */      function next();      /**       * Check if there is a current element after       * calls to rewind() or next().       */      function hasMore();    }    ?> 

The first interface defined is Traversable. This interface is an empty abstract interface that cannot be implemented alone by your code. It is used by PHP's internals to determine which constructs can be used with foreach. When you implement classes that will be usable by foreach, you must implement the Iterator interface, which itself implements Traversable.


Both these interfaces, Traversable and Iterator, are built in to PHP. You should not redeclare these interface definitions in your own code; doing so gives you this error: "Fatal error: Cannot redeclare class.''

Note that most of the methods defined in the Iterator interface share a name with built-in procedural functions used for manipulating arrays. The function rewind() sets PHP's internal array pointer to the first element in an array. current() returns the item in the array to which the pointer currently points. key() returns the key of the current item. next() advances the pointer to the next item. When iterating over an Iterator object, PHP expects these methods to serve the same purpose. This implies that when extending Iterator, you will need to have a private member variable that tracks the current location of the pointer. The hasMore() should return true or false to indicate whether the current item is the last item in the list.

The following code demonstrates what is happening internally when you use foreach with a class implementing Iterator:

   <?php    /* equivalent to foreach($objIt as $key => $member)    $objIt = new MyIterator();    for($objIt->rewind(); $objIt->hasMore(); $objIt->next()) {      $key = $objIt->key();      $member = $objIt->current();    }    ?> 

Now that you understand what this interface does, it's time to put it to work. The CollectionIterator class will be used to iterate over a Collection object.

The CollectionIterator Class

Recall that as does an array, our Collection class stores members in a position using either a string key or an automatically assigned integer. In the previous chapter you created a method called keys() but never really did much with it. Recall that the keys() method returns an array representing all the keys in the Collection. The CollectionIterator class will use this method to get a list of the items in the Collection and provide a means for iterating over them.

The code for class.collectioniterator.php is straightforward and is shown in the following sample.

   <?    class CollectionIterator implements Iterator {       private $_collection;       private $_currIndex = 0;       private $_keys;       function __construct(Collection $objCol) {         $this->_collection = $objCol;         $this->_keys = $this->_collection->keys();       }       function rewind() {         $this->_currIndex = 0;       }       function hasMore() {         return $this->_currIndex < $this->_collection->length();       }       function key() {         return $this->_keys[$this->_currIndex];       }       function current() {         return $this->_collection->getItem($this->_keys[$this->_currIndex]);       }       function next() {         $this->_currIndex++;       }    }    ?> 

By stating that the class implements Iterator, you are required to declare all the methods defined in the Iterator interface. The member variable $_collection holds a reference to the Collection over which you are iterating. $_currIndex is the internal variable that allows the class to maintain a pointer into the Collection that will be used by the methods. Actually, it maintains a pointer into the $_keys array.

The $_keys array holds the return value of Collection::keys() and will be used to fetch the requested member of the Collection. Each call to next() increments $_currIndex by one. Calls to current() and key() use the value of $_currIndex to determine the key of the item to be fetched. Because the actual keys of the Collection may or may not be numeric, you need to have some sort of numerically ordered list to use when iterating. To solve this, you can use Collection::keys() to get a numerically indexed array of the items in the Collection, and reference the value of that array to get the actual key used in the Collection. The following example might make this a little clearer:

   <?php    require_once ("class.collectioniterator.php");    require_once ("class.collection.php");    /* Add some items to the Collection */    $objCol = new Collection();    $objCol->addItem(new Foo(), "foo1");  //key = "foo1"    $objCol->addItem(new Bar(), "bar1");  //key = "bar1"    $objCol->addItem(new FooBar());       //key = 0    $objCol->addItem(new FooBar());       //key = 1    /* Instantiate a CollectionIterator */    $objIt = new CollectionIterator($objCol);    /* $objIt->_keys now looks like this:     * $objIt->_keys[0] = "foo1";     * $objIt->_keys[1] = "bar1";     * $objIt->_keys[2] = 0;     * $objIt->_keys[3] = 1;     */    $objIt->rewind();               //$_currIndex = 0;    $objFoo = $objIt->current();    //$_currIndex = 0, current key = "foo1"                                    //$objFoo = the first Foo object    $objIt->next();                 //$_currIndex = 1;    $objBar = $objIt->current();    //$_currIndex = 1, current key = "bar1"                                    //$objBar = the Bar object    $objIt->next();                    //$_currIndex = 2;    $objFooBar = $objIt->current();    //$_currIndex = 2, current key = 0,                                       //so $objFooBar is the first FooBar object    ?> 

From this example, you can see how the $_keys array and the $_currIndex variable work to enable the methods to keep track of what's going on.

The code you've written so far still doesn't allow you to use foreach on a Collection, but just on a CollectionIterator. To get to this final step, you'll need to learn one more built-in abstract interface.

Professional PHP5 (Programmer to Programmer Series)
Professional PHP5 (Programmer to Programmer Series)
Year: 2003
Pages: 182
BUY ON AMAZON © 2008-2017.
If you may any questions please contact us: