PHP Iterators
If you’ve spent any significant amount of time coding in PHP you’re most likely familiar with PHP’s foreach loop syntax. In simple terms, a foreach loop is an easy way to iterate over the elements of an array. Chances are if you’re reading this you already know that. What you may not know, however, is that it is possible to iterate objects with a foreach loop. Assuming you have some collection class (an object that stores some number of elements in an organized manner) you can iterate over its elements just like you can the elements of an array.
$myCollection = new Collection(); //arbitrary collection class
foreach($myCollection as $element)
{
//do something with $element
}
In order to do this however, you must design your class in such a way as to let PHP know that it supports iteration. As is the case with many other languages, you do this by implementing an interface. An interface is basically a class containing only abstract methods (constants are allowed as well). A class implementing a certain interface must provide the definitions for those abstract methods. The interface we need in this case is called Iterator and has 5 methods that must be implemented:
class MyIterator implements Iterator
{
//Returns the current element
function current()
{
}
//Returns the current key (position in the iterator)
function key()
{
}
//Moves to the next element (returns void)
function next()
{
}
//Resets the iterator
function rewind()
{
}
/*Returns true of the current position in the
iterator exists. False otherwise */
function valid()
{
}
}
From these methods you can derive that the underlying implementation of a PHP foreach loop is something like this:
$i = new MyIterator();
//make sure the iterator is in the starting position
$i->rewind();
while($i->valid())
{
//get the current element and key
$value = $i->current();
$key = $i->key();
//iterator to the next element
$i->next();
}
//equivalent foreach loop
foreach($i as $key => $value)
{
}
Now that we know what the methods do it is time to implement them. The class we’ll implement will be a stripped down version of a linked list that supports only an add method (plus the iterator methods):
class LinkedList implements Iterator
{
private $head;
private $last;
private $count;
private $curr; //iterator counter
private $currNode; //iterator position
function __construct()
{
$this->head = null;
$this->last = null;
$this->count = 0;
$this->curr = 0;
$this->currNode = $this->head;
}
function add($v)
{
$newNode = new Node();
$newNode->value = $v;
if($this->head == null)
{
$this->head = $newNode;
$this->last = $this->head;
}
else
{
$this->last->next = $newNode;
$this->last = $this->last->next;
}
$this->count++;
}
function current()
{
echo "LinkedList::current \n";
return $this->currNode->value;
}
function key()
{
echo "LinkedList::key \n";
return $this->curr;
}
function next()
{
echo "LinkedList::next \n";
$this->currNode = $this->currNode->next;
$this->curr++;
}
function rewind()
{
echo "LinkedList::rewind \n";
$this->currNode = $this->head;
}
function valid()
{
echo "LinkedList::valid \n";
if($this->curr >= $this->count)
return false;
return true;
}
}
class Node
{
public $value;
public $next;
function __construct()
{
$this->value = null;
$this->next = null;
}
}
Notice that I’ve added an echo to each iterator method in the LinkedList class; I’ll explain why later. In case you’re unaware, a linked list is just a series of ‘nodes’. Each node contains a value and a reference to the next node in the list. This pseudo code demonstrates how to traverse a linked list, outputting the values as we go:
Node current := head; while current != null print current->value current := current->next endwhile
Since, however, our linked list class implements Iterator we can traverse it using a foreach loop:
$list = new LinkedList();
$list->add('a');
$list->add('b');
$list->add('c');
foreach($list as $key => $value)
{
echo $key . ' : ' . $value . "\n";
}
Here is the output for the above code:
LinkedList::rewind LinkedList::valid LinkedList::current LinkedList::key 0 : a LinkedList::next LinkedList::valid LinkedList::current LinkedList::key 1 : b LinkedList::next LinkedList::valid LinkedList::current LinkedList::key 2 : c LinkedList::next LinkedList::valid
The above shows the position and value of each element in our list, as well as the order each iterator method is called.
P.S.
This is the first part in a three part series. The next part will be published soon.