6.2 Implementing the Iterator Interface

 <  Day Day Up  >  

In the previous example, an SPL class was used as an iterator. However, it's also useful to write your own iterators. This isn't difficult ”iterators are just PHP classes that implement the Iterator interface.

The Iterator interface is a set of five methods : rewind( ) , valid( ) , key( ) , current( ) , and next ( ) . (The names are similar to the old PHP array iteration functions, but they don't act identically, so don't confuse them.) These methods tell PHP how to pull the next item from your list, when there are no additional items, how to reset the list and start over from the beginning, and so forth.

When iterating over an object using foreach( ) , the methods are called like this:

 $it = new MyIterator; // MyIterator is an class that implements Iterator for ($it->rewind( ); $it->valid( ); $it->next( )) {     $key = $it->key( );     $value = $it->current( );     // code inside the foreach starts here:     print "$key: $value\n"; } unset($it); 

You must implement all five methods. However, depending on how your class operates, some of them can be empty.

Table 6-2 describes what each method does and what values they need to return. You cannot pass any information to these methods; instead, you must store data in object properties.

Table 6-2. Iterator interface methods

Method

Description

Returns

rewind( )

Resets iterator list to its start

Void (nothing)

valid( )

Says if there are additional items left in the list

true if additional items; false otherwise

next( )

Moves iterator to the next item in the list

Void (nothing)

key( )

Returns the key of the current item

Mixed

current( )

Returns the current item

Mixed


Example 6-1 demonstrates an implementation of DirectoryIterator in PHP.

Example 6-1. Implementing DirectoryIterator in PHP
 // Class is called MyDirectoryIterator to prevent a  // namespace clash with SPL's DirectoryIterator. class MyDirectoryIterator implements Iterator {     protected $dir;     protected $path;     protected $key;     protected $file;     protected $valid;          public function _ _construct($path) {         $this->dir = opendir($path);         $this->path = $path;     }     public function _ _destruct( ) {         closedir($this->dir);     }     protected function readDir( ) {         return ($this->file = readdir($this->dir)) !=  = false;     }     public function rewind( ) {         rewinddir($this->dir);         $this->key = 0;         $this->valid = $this->readDir( );     }     public function valid( ) {         return $this->valid;     }     public function key( ) {         return $this->key;     }     public function current( ) {         return $this->file;     }     public function next( ) {         $this->key++;         $this->valid = $this->readDir( );     } } 

The constructor takes a pathname, just as DirectoryIterator does, and stores the directory handle in a protected property named $dir . It also saves the path, but this property isn't used right now ”it's for an example later in the chapter. (If only all programs could be written with this kind of uncanny foresight.) The destructor closes the handle using closedir( ) when the iterator is used up.

The rewind( ) method calls rewinddir( ) on the handle to restore the internal directory pointer to the start. It also sets the count to . Since there's no natural notion of a key for a directory file, this class mimics the behavior of DirectoryIterator . The first file is given a key of , the next gets a key of 1 , and so forth. Last, it reads in a file by calling readDir( ) and sets $valid to the result.

The readDir( ) method calls the PHP function readdir( ) and saves the result in the $file property. If this function returns false , then readDir( ) also returns false ; otherwise, readDir( ) returns true . Although it's still necessary to strictly check the return value of readdir( ) , you can isolate this comparison to a single location within your entire code base. When common code isn't duplicated again and again, it's easier to repair bugs because you can fix them all at once.

Using the PHP directory functions, there's no way to check if a directory has another file without trying to retrieve the file. Therefore, valid( ) just returns the value of the valid property that's set in readDir( ) .

The key( ) and current( ) methods return the value stored in the object properties count and current . Since rewind( ) is called before either of these are accessed, they will always be set.

The next( ) method is quite similar to rewind( ) . It also reads in a new file and sets $valid accordingly , but instead of resetting $key to , it increments it by 1.

Use MyDirectoryIterator exactly how you use DirectoryIterator :

 $dir = new MyDirectoryIterator('/www/www.example.com/'); foreach ($dir as $file) {     print "$file\n"; }  .   ..   email.html   images   includes   index.html   search.html  

With this implementation, you can't add additional methods, such as isDir( ) , to the class, because the class returns the filename instead of a file object, just like DirectoryIterator . However, you can modify MyDirectoryIterator to create a MyFile object that allows you to call methods on $file .

 <  Day Day Up  >  


Upgrading to PHP 5
Upgrading to PHP 5
ISBN: 0596006365
EAN: 2147483647
Year: 2004
Pages: 144

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net