Let's kickstart this blogpost by defining what an iterator actually is. According to wikipedia, an iterator is:
"an object which allows a programmer to traverse through all the elements of a collection, regardless of its specific implementation."
A collection can pretty much be anything. The most obvious sources would be arrays, but other than that, iterations can be done over database resultsets, strings, datetime intervals, directories, file content and XML listings, to name a few. The real benefit of using a standard iterator implementation, is that every implemented iterator does its job obeying a standard interface. Whether said collection is a database resultset or a directory structure; it can be iterated using the same method names. And that's neat, because it will save us developers a trip to the manual on many occasions (... yeah it does... just admit it... ).
SPL
SPL, or the Standard PHP Library:
"is a collection of interfaces and classes that are meant to solve standard problems and implements some efficient data access interfaces and classes."
The SPL extension is available and compiled by default since PHP 5.0.0 and since PHP 5.3 it can no longer be disabled, making it a standard asset to your (or more importantly: your host's) PHP installation. One of the great benefits of SPL, is the way it handles Iterators.
The Iterator interface is defined as listed below. Obviously meaning that every time you implement an Iterator, you have to at least implement the methods mentioned.
PHP:
<?php
interface Iterator
{
// Return the key of the current element.
public function key();
// Return the current element.
public function current();
// Move forward to next element.
public function next();
// Check if there is a current element after calls to rewind() or next().
public function valid();
// Rewind the Iterator to the first element.
public function rewind();
}
?>
When you instantiate your Iterator object, the following methods will be called on the object you iterate:
- __construct() (if you implement a constructor, for example to preset some class variables)
- rewind()
- valid()
- current() // if valid() evaluated to true
Iterating over your collection (using foreach) will then call
- next()
- valid()
- current() // if valid() evaluated to true
Implementing your own iterator
SPL provides many iterators that are waiting for you to be used. An extensive list can be found
here. On top of handling iteration for you, many of these provide additional functionality. The ArrayIterator, for example, gives you an extensive range of methods to run on your collection; like sorting, setting and seeking values. Also available are more complex iterators,like those that handle recursion for you. Instead of getting in depth with all that's possible using SPL's iterators (for that I would recommend visiting SPL's
homepage) I would like to point out how you can implement your very own iterator.
Despite all that's readily available for you to be used, it's still thinkable that you'll encounter situations that just beg for a different kind of iterator to be implemented. I mean: wouldn't you just love to loop over a string and give each character a different color? That's something I run into each and every single day. Here's how it can be done.
First we implement our very own StringIterator, which is basically a class that provides means to loop over each character of a string separately.
PHP:
<?php
class StringIterator implements Iterator
{
protected $collection;
protected $pointer_position;
public function __construct( $collection )
{
$this->collection = $collection;
}
public function key()
{
return $this->pointer_position;
}
public function current()
{
return $this->collection[$this->pointer_position];
}
public function next()
{
$this->pointer_position++;
}
public function rewind()
{
$this->pointer_position = 0;
}
public function valid()
{
return strlen( $this->collection ) > $this->pointer_position;
}
}
?>
Time for some cheerful colors. By extending our very own StringIterator we will be able to add alternative behaviour. Editing the current character can be achieved by wrapping the initial current() returnvalue in a custom function.
PHP:
<?php
class ColorfulStringIterator extends StringIterator
{
public function current( )
{
return $this->randomColor( parent::current( ) );
}
private function randomColor( $character )
{
$color = sprintf( "%02X%02X%02X", mt_rand( 0, 255 ), mt_rand( 0, 255 ), mt_rand( 0, 255 ) );
return '<span style="color:#'.$color.'">'.$character.'</span>';
}
}
?>
Now we will be able to iterate over our collection:
PHP:
<?php
$collection = 'Lorem ipsum dolor sit amet, consectetuer adipiscing elit.';
// using foreach
foreach( new ColorfulStringIterator( $collection ) as $current_element )
{
echo $current_element;
}
// using for
for( $iterator = new ColorfulStringIterator( $collection ); $iterator->valid(); $iterator->next() )
{
echo $iterator->current();
}
?>
Because of iterator standardization, we can then still use additional wrappers. An example would be the LimitIterator, preventing you from iterating out of pre-defined bounds:
PHP:
<?php
$collection = 'Lorem ipsum dolor sit amet, consectetuer adipiscing elit.';
foreach( new LimitIterator( new ColorfulStringIterator( $collection ), 0, 10 ) as $current_element )
{
echo $current_element;
}
?>
Conclusion
To sum this all up: SPL provides great means to standardize the way in which iteration is achieved. Using these standardizations allows you to fully profit from that what is readily available, and that what you can extend with great ease. I've given you a glance at how to implement your own iterator, and this hopefully inspired you to take a closer look at this useful extension. If you hadn't done so already
