HomePHP Page 2 - Iterators in the Simplest Sense: Traversing Different Data Structures
Extending the scope of iterators: building a file 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.
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.