HomePHP Page 4 - Iterators in the Simplest Sense: Traversing Different Data Structures
More iterators ahead: creating a MySQL result set iterator class - PHP
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.
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!