PHP Articles
Basic & Beginners
Advanced
Database
XML, Webservices
Design Patterns
Ajax
All Articles
Main Menu
Home
About PHP Hacks
Links
Contact Us
Search


The Iterator Pattern PDF Print E-mail
Friday, 28 July 2006
PHP has a history of using functions for iterating, but there is now a push to use objects in PHP 5. The Iterator pattern is not language specific and doesn’t need any language enhancements to create. The only added benefit however is the usage in foreach loops with the SPL Iterator interface, but more on that later.

The Iterator Pattern allows for accessing the current element and allowing the retrieval of additional elements in a loop routine or manually in a code block. The Iterator pattern is not constraining to the class for which it holds the data. An object can also aggregate the iterator from outside its class providing iteration while also keeping from implementing the iterator as part of the object. Aggregation of the iterator is a better practice for optimizing overhead for developers who won’t use the main class for implementing the main Iterator methods.

The iteration in classes is not as simple as collecting all of the data and passing it off to the developer. This action doesn’t take in consideration the many data sources that could be used to gather the data in the first place. Including the initial iteration to collect and append the data to the array, which is not a optimal practice. With the Iterator pattern, you should only collect the information, as you need it for when it needs to be passed to save memory.

The Iterator pattern allows you to control how the iteration operates. It makes the iteration go smoothly and easier for the developer as the method names are standard for each Iterator object and won’t need to reference a manual. A developer would be able to take any iteration and know that what method to call.

The SPL Iterator Interface

This Iterator interface uses the SPL Iterator as a reference. All methods are public; therefore the implemented method must also be public even if not used.

interface Iterator
{
    function key();

    function current();

    function next();

    function valid();

    function rewind();
}

Given the confusion with using key(), the number of iterations is often passed as the key. If you have pairs of values, it would also make sense to use the key method. It is not common practice to send the first element as the key and the rest as current. Data passed from key and current should be documented completely. The other method names shouldn’t be difficult to understand.

The SPL IteratorAggregate Interface

You should only pass the data that is needed to the Iterator, if the data is stored in the main class.

Iterator Aggregation allows you to develop without rewriting a lot of the same code for classes that need it. It is simple to implement with only one method in the interface.

It allows you to aggregate to PHP extensions that implement the Iterator pattern. If you need to pass XML, you can create a new SimpleXML class and pass it with the XML giving XML iteration without having to implement it yourself.

interface IteratorAggregate
{
    function getIterator();
}

The method getIterator must return an Iterator object.

Implementing An Array Iterator

The best way to learn iterators is to implement something simple, like array iteration. PHP SPL already supports array iteration using ArrayIterator and we will implement the functionality again, but ArrayIterator should be used for development.

class MyArrayIterator implements Iterator
{
    protected $dataStore = null;

    // Array hinting was added in 5.1.x,
    // in PHP 5.0.x, is_array($data) should be used
    public function __construct(array $data)
    {
        if(is_array($data)) { // For PHP 5.0.x
            $this->dataStore = $data;
        }
    }

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

    public function current()
    {
        return current($this->dataStore);
    }

    public function next()
    {
        return next($this->dataStore);
    }

    public function rewind()
    {
        return reset($this->dataStore);
    }

    public function valid()
    {
        // When Next passes over the end of the array,
        // current() should return false. Needs to be tested.
        return (bool) $this->current();
    }
}

The code keeps to the array parameters: if the array has a key, then the key is returned and all else is returned in current(). Reusing what PHP gives us, we can implement an Iterator for Arrays with very little overhead and speed reduction as compared to procedural.


$array = array('key1' => 'value1', 'key2' => 'value2');

Is the array, which we will use to test the iterator.

foreach(new MyArrayIterator($array) as $current)
{
    echo $current, " ";
}

Will display: value1 value2.

foreach(new MyArrayIterator($array) as $key => $current)
{
    echo $key, " ", $current, "n”;
}

Will display:

    key1 value1
    key2 value2

Implementing a MySQL Iterator

A once hot topic was using an object to iterate over Mysql rows, however since the addition of PDO, it has ceased in relevance. The method for which to control iteration is to either pass SQL to the iterator or to pass the mysql_query() resource to the iterator. The Iterator object should handle both. The iterator should only control how the data is passed and not how it is received without good reason. It is not difficult to check to test the input into the constructor.

class MysqlIterator implements Iterator
{
    protected $row = null;
    protected $query = null;

    public function __construct($query)
    {
        if(is_string($query)) {
            $this->query = mysql_query($query);
        } else if(is_resource($query)) {
            $this->query = $query;
        }
    }

    public function key() { } // Not Implemented

    public function current()
    {
        if($this->row != null)
        {
            return $this->row;
        }
    }

    public function next()
    {
        $this->row = mysql_fetch_assoc($this->query);
        return $this->row;
    }

    public function rewind()
    {
        $this->row = mysql_data_seek($this->query, 0);
        return $this->row;
    }

    public function valid()
    {
        if($this->row == false) {
            return false;
        }

        return true;
    }
}

Saving the internal data for only storing the current row array. Rewinding will seek to the first row. The optimizations should be quick enough for common use.

Iterator Pattern In Loop Constructs

The Iterator objects are not limited to foreach loops and can be used in other loops. Foreach is common practice, because only the class is required and the other loops need manual work.

To use the Iterator pattern in a while loop, you can make the code look beautiful while not changing anything.

$iterator = new MyArrayIterator();

$iterator->rewind();
while($iterator->valid())
{
    $data = $iterator->current();

    $iterator->next();
}

You could even use the for loop method to put all of the pieces together, which would make more sense for PHP 4.

$iterator = new MysqlIterator();

for($iterator->rewind(); $iterator->valid(); $iterator->next())
{
    $data = $iterator->current();
}

Disadvantages

Iterators don’t save you from poor implementations of the interface methods and poor optimizations. You can bring a script to a halt or add not only a few milliseconds, but unto a few tenths of a second. Using the Iterator Pattern will decrease the speed of the script over the procedural method, but if done right it should be manageable and predictable time decrease.

Not all of the SPL Iterators are in the PHP 5.0, the main classes, which are discussed here, are. For most tasks, you only ever will use Iterator and IteratorAggregate.
Conclusion

Conclusion

The Iterator Pattern is not a replacement for SQL, filtering and limiting should be done at the database. Separation of the iteration and main class functionality adds to the code reuse and speed for which projects can be developed. They are an useful tool for managing data and controlling how a developer has access.

One of the greatest benefits of SPL is its many Iterator Pattern implementations. Most of the SPL is at the C code level, so you can almost count on its speed. The code that is still in PHP will be moved to C, so the later the version the greater the chance that it is at C level. You don’t have to depend on SPL Iterator and IteratorAggregate and can easily port it to PHP 4, but you’ll have to use either the while or for loops instead.

Comments
Add NewSearchRSS
Only registered users can write comments!
 
< Prev

Syndicate


Login Form





Lost Password?
No account yet? Register