Iterators in the Simplest Sense: Traversing Different Data Structures

Here we are again. Welcome to the second tutorial of the series “Iterators in the simplest sense.” Just in case you didn’t know, this series introduces Iterators in PHP 4 – PHP 5, explaining their basic concepts, and teaches you how to use them in practical projects, which can be utilized as part of larger PHP applications.

Introduction

As you’ll probably recall, in the first tutorial of this series, I briefly explained the theory behind Iterators in PHP, and highlighted some of their benefits by constructing an array iterator class. As you know, this array iterator was created as a subclass of the abstract “Iterator” class that I defined at the beginning of the first article, demonstrating that building an iterating programming structure in PHP is not only easy, but also fun and instructive.

Nevertheless, an array iterator class isn’t very useful when evaluated as a standalone structure, isolated from other programming modules that might exist within the same Web application. In accordance with this, the real power of such a structure is leveraged when used as a building block of larger programs, which can use at a lower level some (or all) the methods provided by the array iterator.

In order to illustrate how you can implement an array iterator within your own PHP applications, without sacrificing much of its functionality, over this second installment of the series, I’ll show you how different data structures can be traversed by the same set of abstract methods (called an interface) defined previously within the base abstract “Iterator” class. Following this approach, I’ll provide a common interface for iterating over distinct types of data structures, no matter if they’re only flat text files, arrays or MySQL result sets.

By the end of this tutorial, I hope you’ll have an accurate idea of how to use a generic Iterator structure. I will show how it is useful for traversing different data structures by providing specific implementations for each of the abstract methods defined within the base Iterator class. So, let’s not waste any more time and start building different iterators in PHP!

{mospagebreak title=Extending the scope of iterators: building a file iterator class}

In order to demonstrate how the array iterator you learned in the first article can be used for building a file iterator class, let’s first remind ourselves how the previous classes looked, so it’ll be easier for you to understand the process for constructing the file iterator in question. Here’s the list of classes I defined in the first article:

// base abstract ‘Iterator’ class
class Iterator{
    function Iterator(){
        if(get_class($this)==’Iterator’||!is_subclass_of
($this,’Iterator’)){
            trigger_error(‘This class is abstract. It cannot be
instantiated!’,E_USER_ERROR);
        }
    }
    // abstract ‘current()’ method
    function current(){}
    // abstract ‘prev()’ method
    function prev(){}
    // abstract ‘next()’ method
    function next(){}
    // abstract ‘end’ method
    function end(){}
    // abstract ‘reset()’ method
    function reset(){}
    // abstract ‘seek()’ method
    function seek(){}
    // abstract ‘count()’ method
    function count(){}      
}
// ‘ArrayIterator’ subclass
class ArrayIterator extends Iterator{
    var $data=array();
    function ArrayIterator($data=array()){
        if(!is_array($data)){
            trigger_error(‘Data must be an array’,E_USER_ERROR);
        }
        $this->data=$data;
    }
    // concrete implementation for ‘current()’ method
    function current(){
        if(!$data=current($this->data)){
            trigger_error(‘Error retrieving current
element’,E_USER_ERROR);
        }
        return $data; 
    }
    // concrete implementation for ‘prev()’ method
    function prev(){
        if(!$data=prev($this->data)){
            trigger_error(‘Error retrieving previous
element’,E_USER_ERROR);
        }
        return $data; 
    }
    // concrete implementation for ‘next()’ method
    function next(){
        if(!$data=next($this->data)){
            trigger_error(‘Error retrieving next
element’,E_USER_ERROR);
        }
        return $data; 
    }
    // concrete implementation for ‘end()’ method
    function end(){
        if(!$data=end($this->data)){
            trigger_error(‘Error retrieving last
element’,E_USER_ERROR);
        }
        return $data;
    }
    // concrete implementation for ‘reset()’ method
    function reset(){
        if(!$data=reset($this->data)){
            trigger_error(‘Error retrieving first
element’,E_USER_ERROR);
        }
        return $data;
    }
    // concrete implementation for ‘seek()’ method 
    function seek($pos){
        if(!is_int($pos)&&$pos<0){
            trigger_error(‘Invalid offset’,E_USER_ERROR);
        }
        if(!$data=$this->data[$pos]){
            trigger_error(‘Error seeking element’,E_USER_ERROR);
        }
        return $data;
    }
    // concrete implementation for ‘count()’ method
    function count(){
        if(!$data=count($this->data)){
            trigger_error(‘Error counting
elements’,E_USER_ERROR);
        }
        return $data;
    }
}

Now that you hopefully recalled the signature for the above classes, it’s time to create the corresponding file iterator class that I mentioned before, this time using the same set of methods exposed by the “ArrayIterator” class. Please study the class defined below:

class FileIterator extends ArrayIterator{
    var $data=array();
    function FileIterator($file){
        if(!file_exists($file)){
            trigger_error(‘Invalid input file’,E_USER_ERROR);
        }
        $this->data=file($file);
    }
}

Did you think it was harder to build? No, it wasn’t. As you can see, I created a “FileIterator” class merely by deriving a subclass from the array iterator that I defined previously. In this case, Inheritance plays a very important role. It allowed me to build a class which is capable of traversing a simple text file passed as input parameter, in this case by overriding the corresponding constructor.

As you’ve realized, the remaining methods, such as “prev()”, “next()”, “current()” and so forth, have been inherited from the parent array iterator class, and are used to iterate over the array obtained by the following line:

$this->data=file($file);

Even when I used some basic PHP built-in functions for creating the above “FileIterator” class, its functionality is really remarkable. Just stay away for a moment from thinking of sophisticated applications and focus yourself on simpler ones. If your storage needs are largely covered with flat files, the “FileIterator” class might be used as part of a more complex application, providing a simple but powerful system for navigating back and forth between file lines.

At this point, you saw how easy it is to define a “FileIterator” class, having previously written the respective array iterator. Therefore, setting up an example would be a good idea, because in this way you would see how a sample text file can be traversed by this class. To see how this is done, please read the next section.

{mospagebreak title=Building a concrete example: using the “FileIterator” class}

In order to illustrate the functionality of the “FileIterator” class I built previously, first I’ll create a simple text file, populated with some basic data. Then I will instantiate an object from the corresponding class, so I’m able to use its methods. First of all, here’s the basic “test.txt” text file:

This is the data for line 1
This is the data for line 2
This is the data for line 3
This is the data for line 4
This is the data for line 5
This is the data for line 6
This is the data for line 7
This is the data for line 8
This is the data for line 9
This is the data for line 10

And next, you can see the PHP script that traverses the above text file:

// instantiate ‘FileIterator’ object
$fIterator=&new FileIterator(‘test.txt’);
// display first line of file
echo $fIterator->reset();
// display current line of file
echo $fIterator->current();
// display next line of file
echo $fIterator->next();
// display final line of file
echo $fIterator->end();
// display previous line of file
echo $fIterator->prev();
// seek a line within file
echo $fIterator->seek(2);
// count number of lines in file
echo $fIterator->count();

The above snippet might seem like a trivial example, but in fact it’s demonstrating the ease of traversing a particular text file. Of course, here I populated the sample flat file with basic data, but think about the possible implementations for this “FileIterator” class, where file data has to be accessed either in a linear or random mode. Without a doubt, this class can be pretty helpful within a PHP application, particularly considering its ease and understandable source code.

Right, now you saw how simple it is to build a “FileIterator” class, by utilizing as a base class the pertinent array iterator you learned before. However, there are still more examples to show you, in order to continue demonstrating the versatility of iterators in PHP.

For this reason, in the next few lines I’ll define a result set iterator class, which comes in very handy for traversing MySQL data sets. Curious about how this is achieved? Fine, keep on reading to learn more.

{mospagebreak title=More iterators ahead: creating a MySQL result set iterator class}

Having available an array iterator class, which is seated on top of the sample classes hierarchy, defining a MySQL result set iterator class is actually a straightforward task. All I have to do is derive another subclass from the respective parent array iterator class, and the job is almost done. Since I want to create a full object-oriented example, I’ll include a couple of additional classes, where the first one is a MySQL abstraction class and the second one is a MySQL result set processing class.

If you’ve already read some of my previous PHP articles, you’ll find these classes very familiar, so they shouldn’t be particularly hard to understand. That said, here is the signature for the corresponding MySQL wrapping class:

class MySQL {
    var $conId; // connection identifier
    var $host; // MySQL host
    var $user; // MySQL username
    var $password; // MySQL password
    var $database; // MySQL database
    // constructor
    function MySQL($options=array()){
    // validate incoming parameters
        if(count($options)>0){
            foreach($options as $parameter=>$value){
                if(empty($value)){
                    trigger_error(‘Invalid parameter
‘.$parameter,E_USER_ERROR);
                }
                $this->{$parameter}=$value;
            }
            // connect to MySQL
            $this->connectDB();
        }
        else {
            trigger_error(‘No connection parameters were
provided’,E_USER_ERROR);
        }
    }
    // connect to MYSQL server and select database
    function connectDB(){
        if(!$this->conId=mysql_connect($this->host,$this-
>user,$this->password)){
            trigger_error(‘Error connecting to the
server’,E_USER_ERROR);
        }
        if(!mysql_select_db($this->database,$this->conId)){
             trigger_error(‘Error selecting
database’,E_USER_ERROR);
        }
    }
    // perform query
    function query($query){
        if(!$this->result=mysql_query($query,$this->conId)){
            trigger_error(‘Error performing query
‘.$query,E_USER_ERROR);
        }
        // return new Result object
        return new Result($this,$this->result); 
    }
}

Having listed the source code for this simple MySQL abstraction class, I’ll go ahead with showing the code for the respective MySQL “Result” class. Here you have it:

class Result {
    var $mysql; // instance of MySQL object
    var $result; // result set
    function Result(&$mysql,$result){
        $this->mysql=&$mysql;
        $this->result=$result;
    }
    // fetch row
    function fetchRow(){
        return mysql_fetch_array($this->result,MYSQL_ASSOC);
    }
    // count rows
    function countRows(){
        if(!$rows=mysql_num_rows($this->result)){
            trigger_error(‘Error counting rows’,E_USER_ERROR);
        }
        return $rows;
    }
    // count affected rows
    function countAffectedRows(){
        if(!$rows=mysql_affected_rows($this->mysql->conId)){
            trigger_error(‘Error counting affected
rows’,E_USER_ERROR);
        }
        return $rows;
    }
    // get ID from last inserted row
    function getInsertID(){
        if(!$id=mysql_insert_id($this->mysql->conId)){
            trigger_error(‘Error getting ID’,E_USER_ERROR);
        }
        return $id;
    }
    // seek row
    function seekRow($row=0){
        if(!mysql_data_seek($this->result,$row)){
            trigger_error(‘Error seeking data’,E_USER_ERROR);
        }
    }
    function getQueryResource(){
        return $this->result;
    }
}

Finally, now that you know how the above complementary classes were appropriately defined, here’s the signature for the “ResultIterator” class:

class ResultIterator extends ArrayIterator{
    function ResultIterator($result){
        if(get_resource_type($result)!=’mysql result’){
            trigger_error(‘Input data must be a MySQL result
set’,E_USER_ERROR);
        }
        while($row=mysql_fetch_row($result)){
            $this->data[]=implode(”,$row);
        }
    }
}

As shown above, the “ResultIterator” class is created by deriving a subclass from the base array iterator. Since its definition is extremely simple, the only thing worth noting here is the checking process that is performed by the constructor, in order to make sure that only MySQL result sets are passed in as incoming arguments. Finally, the entire result set is stored in the “$this->data” array, which is very convenient for traversing with minor hassles.

Of course, as with everything in life, this versatility comes at a cost: if a SQL query returns a large number of database records, it might cause some performance issues, since the data will be stored in server memory. To solve this problem, you can either write an iterator class that only acts on native MySQL datasets without using additional arrays, or use the approach I showed you before, in case your database tables only hold a relatively small number of records.

Having clarified this performance point, below is an example of how to use the “ResultIterator” class:

// include class files
require_once ‘mysqlclass.php’;
require_once ‘resultclass.php’;
// connect to MySQL
$db=&new MySQL(array(‘host’=>’host’,'user’=>’user’,'password’=>’password’,
‘database’=>’database’));
// get result set
$result=$db->query(‘SELECT * FROM mytable’);
// use ‘ResultIterator’ class
$rIterator=&new ResultIterator($result->getQueryResource());
// display first row of the result set
echo $rIterator->reset();
// display current row of the result set
echo $rIterator->current();
// display next row of the result set
echo $rIterator->next();
// display final row of the result set
echo $rIterator->end();
// display previous row of the result set
echo $rIterator->prev();
// seek row within result set
echo $rIterator->seek(4);
// count number of rows in result set
echo $rIterator->count();

As shown in the example above, traversing a MySQL result set back and forth is a simple process. It’s just a matter of calling the corresponding methods of the “ResultIterator” class, in order to move the internal data pointer (in fact it’s an array pointer), across the whole dataset. Notice the functionality encapsulated within these methods, which can be used together, in order to perform more complex operations inside a specific PHP application.

At this stage, hopefully you learned the basics of Iterators in PHP, accompanied by numerous hands-on examples of how to build several PHP classes, in order to traverse different data structures. Of course, the examples I showed here should be considered introductory samples that can be used for starting quickly with using Iterators in PHP.

To wrap up

Now this article has concluded. During this second part of this series, I demonstrated the process for building different PHP 4 iterators in a friendly way. I hope the code samples you saw here can be helpful for expanding your overall knowledge on Design Patterns, as well as on object-oriented programming in PHP.

Throughout the last installment of the series, I’ll cover Iterators in PHP 5, which is really an interesting topic, due to the great capabilities offered by the SPL (Standard PHP Library). See you in the last tutorial!

Google+ Comments

Google+ Comments