Login or Register Now   Email:  Password:   

Introduction to SPL

Introduction to Standard PHP Library (SPL)

By Kevin Waterson

Contents

  1. What is SPL
  2. What are Iterators
  3. Directory Iterator
  4. Extending the DirectoryIterator
  5. ArrayObject
  6. Array Iterator
  7. RecursiveArrayIterator
  8. Something useful
  9. Overloading
  10. Filter Iterator
  11. Simple XML Iterator
  12. Caching Iterator
  13. Limit Iterator
  14. SplFileObject
  15. Conclusions
  16. Credits

What is SPL.

SPL provides a standard set of interfaces for PHP5. The aim of SPL is to implement some efficient data access interfaces and classes for PHP. Functionally it is designed to traverse aggregate structures (anything you want to loop over). These may include arrays, database result sets, xml trees, directory listings or any list at all. Currently SPL deals with Iterators. To see all the classes available to SPL, this simple snippet will show you.

<?php
// a simple foreach() to traverse the SPL class names
foreach(spl_classes() as $key=>$value)
        {
        echo 
$key.' -&gt; '.$value.'<br />';
        }
?>

This will provide you with a list something like this:

  • AppendIterator -> AppendIterator
  • ArrayIterator -> ArrayIterator
  • ArrayObject -> ArrayObject
  • BadFunctionCallException -> BadFunctionCallException
  • BadMethodCallException -> BadMethodCallException
  • CachingIterator -> CachingIterator
  • Countable -> Countable
  • DirectoryIterator -> DirectoryIterator
  • DomainException -> DomainException
  • EmptyIterator -> EmptyIterator
  • FilterIterator -> FilterIterator
  • InfiniteIterator -> InfiniteIterator
  • InvalidArgumentException -> InvalidArgumentException
  • IteratorIterator -> IteratorIterator
  • LengthException -> LengthException
  • LimitIterator -> LimitIterator
  • LogicException -> LogicException
  • NoRewindIterator -> NoRewindIterator
  • OuterIterator -> OuterIterator
  • OutOfBoundsException -> OutOfBoundsException
  • OutOfRangeException -> OutOfRangeException
  • OverflowException -> OverflowException
  • ParentIterator -> ParentIterator
  • RangeException -> RangeException
  • RecursiveArrayIterator -> RecursiveArrayIterator
  • RecursiveCachingIterator -> RecursiveCachingIterator
  • RecursiveDirectoryIterator -> RecursiveDirectoryIterator
  • RecursiveFilterIterator -> RecursiveFilterIterator
  • RecursiveIterator -> RecursiveIterator
  • RecursiveIteratorIterator -> RecursiveIteratorIterator
  • RecursiveRegexIterator -> RecursiveRegexIterator
  • RegexIterator -> RegexIterator
  • RuntimeException -> RuntimeException
  • SeekableIterator -> SeekableIterator
  • SimpleXMLIterator -> SimpleXMLIterator
  • SplFileInfo -> SplFileInfo
  • SplFileObject -> SplFileObject
  • SplObjectStorage -> SplObjectStorage
  • SplObserver -> SplObserver
  • SplSubject -> SplSubject
  • SplTempFileObject -> SplTempFileObject
  • UnderflowException -> UnderflowException
  • UnexpectedValueException -> UnexpectedValueException

What are iterators?

An Iterator is an object that traverses a structure eg: an array or a directory listing or possibly a set of database result sets or other resource. This is not an accurate discription, but more will become clear later by way of example. There are different types of iterators for dealing with different types of data such as array Iterators, Directory Iterators and more. Here we will begin to get familiar with them beginning with the DirectoryIterator. What is important to note is they can all be accessed with a standard interface. This means that regardless of the data type, access to the information is standardised. This is a real step forward for PHP.

Directory Iterator

The DirectoryIterator in its simplest form will do some magic on its own, this built in class can be extended to do alot more. Here we begin with a simple directory listing. In the bad old days we needed to do something like this to produce a list of files in a directory.

<?php 
  $hdl 
opendir('./'); 
  while (
$dirEntry readdir($hdl))
    {
    if (
substr($dirEntry01) != '.')
        {
        if(!
is_file($dirEntry))
            {
            continue;
            }
        
$listing[] = $dirEntry;
        }
    }
  
closedir($hdl);
  foreach(
$listing as $my_file)
    {
    echo 
$my_file.'<br />';
    }
?>

Now consider a object oriented approach with SPL

<?php

try{
  
/*** class create new DirectoryIterator Object ***/
    
foreach ( new DirectoryIterator('./') as $Item )
        {
        echo 
$Item.'<br />';
        }
    }
/*** if an exception is thrown, catch it here ***/
catch(Exception $e){
    echo 
'No files Found!<br />';
}
?>

The code above will produce a simple list of all files and directories within a current working directory that is passed directly to the __construct(). This is all good, but we can do so much more. Lets create a file called foo.txt with some text in it. For windows users simply use notepad and for *nix users simply
echo 'This is a line of text' > foo.txt
We can see the available method from the DirectoryIterator like this:

<?php
/*** list all class methods ***/
foreach( get_class_methods(DirectoryIterator) as $methodName)
        {
        echo 
$methodName.'<br />';
        }
?>

Now we have a full list of available methods to the DirectoryIterator class. The list will look something like this:

  • __construct
  • getFilename
  • getBasename
  • isDot
  • rewind
  • valid
  • key
  • current
  • next
  • __toString
  • getPath
  • getPathname
  • getPerms
  • getInode
  • getSize
  • getOwner
  • getGroup
  • getATime
  • getMTime
  • getCTime
  • getType
  • isWritable
  • isReadable
  • isExecutable
  • isFile
  • isDir
  • isLink
  • getFileInfo
  • getPathInfo
  • openFile
  • setFileClass
  • setInfoClass

Using the listed methods from the above code allows us to see how much information we can get from the class. Lets go over it.


<table>
<?php

foreach(new DirectoryIterator('./' ) as $file )
    {
    if( 
$file->getFilename()  == 'foo.txt' )
        {
        echo 
'<tr><td>getFilename()</td><td> 'var_dump($file->getFilename()); echo '</td></tr>';
    echo 
'<tr><td>getBasename()</td><td> 'var_dump($file->getBasename()); echo '</td></tr>';
        echo 
'<tr><td>isDot()</td><td> 'var_dump($file->isDot()); echo '</td></tr>';
        echo 
'<tr><td>__toString()</td><td> 'var_dump($file->__toString()); echo '</td></tr>';
        echo 
'<tr><td>getPath()</td><td> 'var_dump($file->getPath()); echo '</td></tr>';
        echo 
'<tr><td>getPathname()</td><td> 'var_dump($file->getPathname()); echo '</td></tr>';
        echo 
'<tr><td>getPerms()</td><td> 'var_dump($file->getPerms()); echo '</td></tr>';
        echo 
'<tr><td>getInode()</td><td> 'var_dump($file->getInode()); echo '</td></tr>';
        echo 
'<tr><td>getSize()</td><td> 'var_dump($file->getSize()); echo '</td></tr>';
        echo 
'<tr><td>getOwner()</td><td> 'var_dump($file->getOwner()); echo '</td></tr>';
        echo 
'<tr><td>$file->getGroup()</td><td> 'var_dump($file->getGroup()); echo '</td></tr>';
        echo 
'<tr><td>getATime()</td><td> 'var_dump($file->getATime()); echo '</td></tr>';
        echo 
'<tr><td>getMTime()</td><td> 'var_dump($file->getMTime()); echo '</td></tr>';
        echo 
'<tr><td>getCTime()</td><td> 'var_dump($file->getCTime()); echo '</td></tr>';
        echo 
'<tr><td>getType()</td><td> 'var_dump($file->getType()); echo '</td></tr>';
        echo 
'<tr><td>isWritable()</td><td> 'var_dump($file->isWritable()); echo '</td></tr>';
        echo 
'<tr><td>isReadable()</td><td> 'var_dump($file->isReadable()); echo '</td></tr>';
        echo 
'<tr><td>isExecutable(</td><td> 'var_dump($file->isExecutable()); echo '</td></tr>';
        echo 
'<tr><td>isFile()</td><td> 'var_dump($file->isFile()); echo '</td></tr>';
        echo 
'<tr><td>isDir()</td><td> 'var_dump($file->isDir()); echo '</td></tr>';
        echo 
'<tr><td>isLink()</td><td> 'var_dump($file->isLink()); echo '</td></tr>';
        echo 
'<tr><td>getFileInfo()</td><td> 'var_dump($file->getFileInfo()); echo '</td></tr>';
        echo 
'<tr><td>getPathInfo()</td><td> 'var_dump($file->getPathInfo()); echo '</td></tr>';
        echo 
'<tr><td>openFile()</td><td> 'var_dump($file->openFile()); echo '</td></tr>';
        echo 
'<tr><td>setFileClass()</td><td> 'var_dump($file->setFileClass()); echo '</td></tr>';
        echo 
'<tr><td>setInfoClass()</td><td> 'var_dump($file->setInfoClass()); echo '</td></tr>';
        }
}
?>
</table>

The above code will produce results similar to this:

getFilename() string(7) "foo.txt"
getBasename()string(7) "foo.txt"
isDot() bool(false)
__toString() string(7) "foo.txt"
getPath() string(1) "."
getPathname() string(9) "./foo.txt"
getPerms() int(33206)
getInode() int(352035)
getSize() int(23)
getOwner() int(501)
getGroup() int(501)
getATime() int(1159249715)
getMTime() int(1098354402)
getCTime() int(1155517032)
getType() string(4) "file"
isWritable() bool(true)
isReadable() bool(true)
isExecutable() bool(false)
isFile() bool(true)
isDir() bool(false)
isLink() bool(false)
getFileInfo() object(SplFileInfo)#3 (0) {}
getPathInfo() object(SplFileInfo)#3 (0) {}
openFile() object(SplFileObject)#3 (0) {}
setFileClass() NULL
setInfoClass() NULL

What we are seeing is an object oriented approach to dealing with iterations and files. As you can see from the table above, there are simple php functions that match these methods. One of the greatest benifits of this approach is the ability to handle any errors with exceptions. This makes the SPL extension a wholly rounded module and far superior to what we have previously been used to.

We saw at the head of the list, several methods that do not specifically pertain to getting file information. These are listed here:

  • __construct
  • rewind
  • valid
  • key
  • current
  • next
  • __toString

These are the DirectoryIterator class methods that will help us traverse over our object. The __construct() is used as we pass the directory. Below we show how a simple directory traversal may look if we use an Object Oriented approach.


<?php
/*** create a new iterator object ***/
$it = new DirectoryIterator('./');

/*** loop directly over the object ***/
while($it->valid())
    {
    echo 
$it->key().' -- '.$it->current().'<br />';
    
/*** move to the next iteration ***/
    
$it->next();
    }
?>

The above code will iterate directly over the object $it. The use of the valid() method checks that there is a valid member to iterate over and if that is true, then we can display the key, and the current value within the iteration. The use of next() moves us to the next iteration, much in the same was as would happen with $i++. If we wanted to show only directories within our iterator object we could do something like this:

<?php
/*** create a new iterator object ***/
$it = new DirectoryIterator('./');

/*** loop directly over the object ***/
while($it->valid())
        {
        
/*** check if value is a directory ***/
        
if($it->isDir())
                {
                
/*** echo the key and current value ***/
                
echo $it->key().' -- '.$it->current().'<br />';
                }
        
/*** move to the next iteration ***/
        
$it->next();
        }
?>

So, now we have a list of directories within the path. But we could have done this in a more Object Oriented way that would allow us more flexibility. We begin by extending the DirectoryIterator class.

Extending the DirectoryIterator class.

<?php
/*** class definition to extend Directory Iterator class ***/
class DirectoryReader extends DirectoryIterator {

// constructor.. duh!
function __construct($path){
  
/*** pass the $path off to the parent class constructor ***/
  
parent::__construct($path);
}

/*** return the current filename ***/
function current(){
  return 
parent::getFileName();
}

/*** members are only valid if they are a directory ***/
function valid(){
  if(
parent::valid())
    {
    if (!
parent::isDir())
        {
        
parent::next();
        return 
$this->valid();
        }
    return 
TRUE;
    }
  return 
FALSE;
}

// end class

try{
/*** a new iterator object ***/
$it = new DirectoryReader('./');
/*** loop over the object if valid ***/
while($it->valid())
    {
    
/*** echo the current object member ***/
    
echo $it->current().'<br />';
    
/*** advance the internal pointer ***/
    
$it->next();
    }
}

catch(
Exception $e){
    echo 
'No files Found!<br />';
}

?>

The code above demonstrates how we can overload the Iterator methods to reduce the logic within the user code. This creates great opportunities to make portable classes for re-use, thus reducing user code and speeding up development time. Once again the use of Exceptions shows how easy it can be to catch errors and deal with them. We could, of course, use the isFile() method in place of isDir() to show only files. The possibilities are endless.

Lets go over what we have done here. First we have created a small class to extend the internal DirectoryIterator class. In the constructor we have passed the $path variable and called the parent class constructor. The parent class is of course the SPL DirectoryIterator class.

The valid method checks if the file meets a criteria. This begins with the parent::valid checking to see that the current iteration has a value. From there we check if the current Iteration is a directory by calling the parent::isDir() method. If this is not a directory, the iterator is advanced with the parent::next() method and checks again if the next value is valid. We could have used the FilterIterator to achieve the same goal, but more on that later.

ArrayObject

The ArrayObject allows for external traversal of arrays and to create instances of ArrayIterator. Much like the Directory iterator, we can see the methods available to the Array Iterator with a simple snippet.


<?php
  
foreach(get_class_methods(new ArrayObject()) as $key=>$method)
        {
        echo 
$key.' -> '.$method.'<br />';
        }
?>

Again we see the class methods in a list as follows.

  • 0 -> __construct
  • 1 -> offsetExists
  • 2 -> offsetGet
  • 3 -> offsetSet
  • 4 -> offsetUnset
  • 5 -> append
  • 6 -> getArrayCopy
  • 7 -> count
  • 8 -> getFlags
  • 9 -> setFlags
  • 10 -> asort
  • 11 -> ksort
  • 12 -> uasort
  • 13 -> uksort
  • 14 -> natsort
  • 15 -> natcasesort
  • 16 -> getIterator
  • 17 -> exchangeArray
  • 18 -> setIteratorClass
  • 19 -> getIteratorClass

We will see in the following code snippets, various ways to use some of these methods along with the ArrayIterator class. First a simple ArrayObject and ArrayIterator.

<?php

/*** a simple array ***/
$array = array('koala''kangaroo''wombat''wallaby''emu''kiwi''kookaburra''platypus');

/*** create the array object ***/
$arrayObj = new ArrayObject($array);

/*** iterate over the array ***/
for($iterator $arrayObj->getIterator();
   
/*** check if valid ***/
   
$iterator->valid();
   
/*** move to the next array member ***/
   
$iterator->next())
    {
    
/*** output the key and current array value ***/
    
echo $iterator->key() . ' => ' $iterator->current() . '<br />';
    }
?>

In the above script we have externally traversed the array object with getIterator(). We could have used a foreach on the array and the getInstance() method would be called implicitly. The key() and current() methods also belong to the ArrayIterator Instance. From the above code snippet we get a simple array output as follows

  • 0 => koala
  • 1 => kangaroo
  • 2 => wombat
  • 3 => wallaby
  • 4 => emu
  • 5 => kiwi
  • 6 => kookaburra
  • 7 => platypus

You might be getting the swing of this by now, so to keep it simple we will use the same code from above and append, or add, a value to the array and iterate over it. Note that the ArrayObject::getIterator returns an ArrayIterator instance working on the original ArrayObject instance. It's only method getIterator() is automatically called in iteration, thus if you put an ArrayObject which implements that interface into a foreach() construct that method is being executed automatically. Since the ArrayObject returns an ArrayIterator interface the foreach() Construct will reset that iterator and work on it.


<?php

/*** a simple array ***/
$array = array('koala''kangaroo''wombat''wallaby''emu''kiwi''kookaburra''platypus');

/*** create the array object ***/
$arrayObj = new ArrayObject($array);

/*** append a value to the array ***/
$arrayObj->append('dingo');

/*** iterate over the array ***/
for($iterator $arrayObj->getIterator();
   
/*** check if valid ***/
   
$iterator->valid();
   
/*** move to the next array member ***/
   
$iterator->next())
        {
        
/*** output the key and current array value ***/
        
echo $iterator->key() . ' -> ' $iterator->current() . '<br />';
        }
?>

Now we see that the list has the value 'dingo' on the end of it and we begin to see the usefulness of an Object Oriented approach. Our list looks like this.
0 -> koala
1 -> kangaroo
2 -> wombat
3 -> wallaby
4 -> emu
5 -> kiwi
6 -> kookaburra
7 -> platypus
8 -> dingo

We can sort the array with one of the array sort methods available to the array object. In this instance, we will sort alphabetically using the natcasesort() method.

<?php

/*** a simple array ***/
$array = array('koala''kangaroo''wombat''wallaby''emu''kiwi''kookaburra''platypus');

/*** create the array object ***/
$arrayObj = new ArrayObject($array);

/*** sort alphabetically ***/
$arrayObj->natcasesort();

/*** iterate over the array ***/
for($iterator $arrayObj->getIterator();
   
/*** check if valid ***/
   
$iterator->valid();
   
/*** move to the next array member ***/
   
$iterator->next())
        {
        
/*** output the key and current array value ***/
        
echo $iterator->key() . ' -> ' $iterator->current() . '<br />';
        }
?>

Now we see our array has changed and the output now looks like this:
4 -> emu
1 -> kangaroo
5 -> kiwi
0 -> koala
6 -> kookaburra
7 -> platypus
3 -> wallaby
2 -> wombat
As you can see, the order has changed but not the keys. We could sort them similarly with asort, usort or any of the other sorting methods listed by get_class_methods(). To see a count of the total number of members in our array object is a trivial matter of using the count() method as shown below.

<?php

/*** a simple array ***/
$array = array(koalakangaroowombatwallabyemukiwikookaburraplatypus);

/*** create the array object ***/
$arrayObj = new ArrayObject($array);

/*** echo the total number of elements ***/
echo $arrayObj->count();
?>

We see the return value from the count() method is 8. We may also note that one of the beasts in our array of animals does not belong. This is an array of animals native to Australia (straya), but the kiwi is a New Zealand critter. To remove this undesirable from the array we use theoffsetUnset() method. Lets see how it works.

<?php

/*** a simple array ***/
$array = array('koala''kangaroo''wombat''wallaby''emu''kiwi''kookaburra''platypus');

/*** create the array object ***/
$arrayObj = new ArrayObject($array);
/*** unset the array member ***/
$arrayObj->offsetUnset(5);

/*** loop of the array object ***/
for($iterator $arrayObj->getIterator();
   
/*** check if valid ***/
   
$iterator->valid();
   
/*** move to the next array member ***/
   
$iterator->next())
        {
        
/*** output the key and current array value ***/
        
echo $iterator->key() . ' -> ' $iterator->current() . '<br />';
        }
?>

The above code will produce this output..
0 -> koala
1 -> kangaroo
2 -> wombat
3 -> wallaby
4 -> emu
6 -> kookaburra
7 -> platypus
The offending New Zealand critter has been removed from the array but the array is not re-indexed.

To check for the existance of an offset within the array, we use the offsetExists() method as demonstrated below.

<?php
/*** a simple array ***/
$array = array('koala''kangaroo''wombat''wallaby''emu''kiwi''kookaburra''platypus');

/*** create the array object ***/
$arrayObj = new ArrayObject($array);

 if (
$arrayObj->offsetExists(3))
    {
    
/*** unset the key ***/
    
echo 'Offset Exists</br />';
    }
?>

The above snippet tells us the offset Exists and runs the code in the if{} block. This has the same functionality as array_key_exists(). It is possible to change the value of an array member with the offsetSet method. Here we show how by changing the kiwi to the Australian native bird 'galah'.

<?php
/*** a simple array ***/
$array = array('koala''kangaroo''wombat''wallaby''emu''kiwi''kookaburra''platypus');

try {
    
/*** create the array object ***/
    
$arrayObj = new ArrayObject($array);

    
/*** set the offset of 5 to a new value ***/
    
$arrayObj->offsetSet(5"galah");

    
/*** loop of the array object ***/
    
for($iterator $arrayObj->getIterator();
    
/*** check if valid ***/
    
$iterator->valid();
    
/*** move to the next array member ***/
    
$iterator->next())
            {
            
/*** output the key and current array value ***/
            
echo $iterator->key() . ' -> ' $iterator->current() . '<br />';
            }
    }
catch (
Exception $e)
    {
    echo 
$e->getMessage();
    }
?>

We see that when we iterate over the array object the kiwi has been set to 'galah'. We can also get a value from the array object by using the offsetGet() method.

<?php
/*** a simple array ***/
$array = array('koala''kangaroo''wombat''wallaby''emu''kiwi''kookaburra''platypus');

try {
    
/*** create the array object ***/
    
$arrayObj = new ArrayObject($array);

    
/*** echo the value of the array object 4 ***/
    
echo $arrayObj->offsetGet(4);
    }
catch(
Exception $e)
    {
    echo 
$e->getMessage();
    }
?>

The script above prints 'emu' as this is the value of the offset 4.

There are times you may need a copy of an array to edit or to do comparisons, to this end the method getArrayCopy() is provided for just that purpose. Here we show a simple array copy.

<?php
/*** a simple array ***/
$array = array('koala''kangaroo''wombat''wallaby''emu''kiwi''kookaburra''platypus');

try {
    
/*** create the array object ***/
    
$arrayObj = new ArrayObject($array);

    
/*** create a copy of the array object ***/
    
$arrayObjCopy $arrayObj->getArrayCopy();

    
/*** iterate over the array copy ***/
    
for($iterator $arrayObjCopy->getIterator();
    
/*** check if valid ***/
    
$iterator->valid();
    
/*** move to the next array member ***/
    
$iterator->next())
        {
        
/*** output the key and current array value ***/
        
echo $iterator->key() . ' => ' $iterator->current() . '<br />';
        }
    }
catch (
Exception $e)
    {
    echo 
$e->getMessage();
    }
?>

When you run the above code you will get an error message similar to this..
Fatal error: Call to a member function getIterator() on a non-object in /www/spl.php on line 13
This is because the copy of the array is just a copy of the array, it is NOT an array object that SPL can iterate over, so we must create a new array object as shown here:

<?php
/*** a simple array ***/
$array = array('koala''kangaroo''wombat''wallaby''emu''kiwi''kookaburra''platypus');

try {
    
/*** create the array object ***/
    
$arrayObj = new ArrayObject($array);

    
/*** create a copy of the array object 
    you MUST create a new array object also ***/
    
$arrayObjCopy = new ArrayObject($arrayObj->getArrayCopy());

    
/*** iterate over the array copy ***/
    
for($iterator $arrayObjCopy->getIterator();
    
/*** check if valid ***/
    
$iterator->valid();
    
/*** move to the next array member ***/
    
$iterator->next())
        {
        
/*** output the key and current array value ***/
        
echo $iterator->key() . ' -> ' $iterator->current() . '<br />';
        }
    }
catch (
Exception $e)
    {
    echo 
$e->getMessage();
    }
?>

Now of course, we can safely iterate over the newly create array and array object, the results will be as before:
0 -> koala
1 -> kangaroo
2 -> wombat
3 -> wallaby
4 -> emu
5 -> kiwi
6 -> kookaburra
7 -> platypus

The ArrayObject class also comes with several flags..

  • ARRAY_AS_PROPS
  • STD_PROP_LIST

These flags are quite funky the ARRAY_AS_PROPS constant allows array indexes to be accessed as properties in read/write, while the STD_PROP_LIST allows properties of the object to have their normal functionality. Lets begin with a short demonstration of ARRAY_AS_PROPS.

<?php

?>

ArrayIterator

If you have not read the previous section on ArrayObject it is highly recommended you do so, as this section is really an addition to the previous where we have been using the ArrayIterator to traverse ArrayObjects.. The ArrayIterator makes use of the ArrayObject to traverse arrays and an understanding of this is important. Much like the Directory iterator, we can see the methods available to the Array Iterator with a simple snippet.

<?php

  
foreach(get_class_methods(new ArrayIterator()) as $key=>$method)
        {
        echo 
$key.' -> '.$method.'<br />';
        }
?>

The above snippet will produce a list of methods available to the ArrayIterator class as shown below.

  • 0 -> __construct
  • 1 -> offsetExists
  • 2 -> offsetGet
  • 3 -> offsetSet
  • 4 -> offsetUnset
  • 5 -> append
  • 6 -> getArrayCopy
  • 7 -> count
  • 8 -> getFlags
  • 9 -> setFlags
  • 10 -> asort
  • 11 -> ksort
  • 12 -> uasort
  • 13 -> uksort
  • 14 -> natsort
  • 15 -> natcasesort
  • 16 -> rewind
  • 17 -> current
  • 18 -> key
  • 19 -> next
  • 20 -> valid
  • 21 -> seek

Some of this should look familiar if you have read the DirectoryIterator section. Remember the 'S' in SPL is for 'Standard' and here we see why. From here, using the ArrayIterator is quite simple given any array. Here we iterate over a simple array and output the variables to the browser. The ArrayIterator takes an ArrayObject as its arguement, so, before we can use the ArrayIterator class we must create the ArrayObject to be able to iterate over it.

We have already seen the ArrayIterator in action with the ArrayObject. Lets see a simple array iteration.


<?php
/*** a simple array ***/
$array = array('koala''kangaroo''wombat''wallaby''emu''kiwi''kookaburra''platypus');

try {
    
/*** create a new object ***/
    
$object = new ArrayIterator($array);
    
/*** rewind to the beginning of the array ***/
    
$object->rewind();
    
/*** check for valid member ***/
    
while($object->valid())
        {
        
/*** echo the key and current value ***/
        
echo $object->key().' -&gt; '.$object->current().'<br />';
        
/*** hop to the next array member ***/
        
$object->next();
        }
    }
catch (
Exception $e)
    {
    echo 
$e->getMessage();
    }
?>

Of course, the script above iterates over the array and produces a list of the array keys and values, it shows how we have manually called the ArrayIterator to traverse the array of animals. We could however, call the ArrayIterator implicitly like this:


<?php
/*** a simple array ***/
$array = array('koala''kangaroo''wombat''wallaby''emu''kiwi''kookaburra''platypus');

try {
    
$object = new ArrayIterator($array);
    foreach(
$object as $key=>$value)
        {
        echo 
$key.' => '.$value.'<br />';
        }
    }
catch (
Exception $e)
    {
    echo 
$e->getMessage();
    }
?>

Above we see the ArrayIterator is implicitly used but we the difference is hidden from us. A traditional array traversal allocates every member of the array to memory. The iterator assigns memory for the current element only. This can be a huge benifit when dealing with large arrays.

Array offsets may be checked for and acquired using the offSetExists() and offSetGet() methods as shown below.


<?php
/*** a simple array ***/
$array = array('koala''kangaroo''wombat''wallaby''emu''kiwi''kookaburra''platypus');

try {
    
$object = new ArrayIterator($array);
    if(
$object->offSetExists(2))
        {
        echo 
$object->offSetGet(2);
        }
    }
catch (
Exception $e)
    {
    echo 
$e->getMessage();
    }
?>

The above code checks for the existence of the offset 2 with the offSetExists() method. When it is found, the value of the offset is used to display it using offSetGet(). We can also set and unset array members within the array.


<ul>
<?php
/*** a simple array ***/
$array = array('koala''kangaroo''wombat''wallaby''emu''kiwi''kookaburra''platypus');

try {
    
$object = new ArrayIterator($array);
    
/*** check for the existence of the offset 2 ***/
    
if($object->offSetExists(2))
    {
    
/*** set the offset of 2 to a new value ***/
    
$object->offSetSet(2'Goanna');
    }
   
/*** unset the kiwi ***/
   
foreach($object as $key=>$value)
        {
        
/*** check the value of the key ***/
        
if($object->offSetGet($key) === 'kiwi')
            {
            
/*** unset the current key ***/
            
$object->offSetUnset($key);
            }
        echo 
'<li>'.$key.' - '.$value.'</li>'."\n";
        }
    }
catch (
Exception $e)
    {
    echo 
$e->getMessage();
    }
?>
</ul>

Now we see some interesting behaviour along with our expected behaviour. Using the offsetSet() method the value at the offset of 2 has been changed from wombat to Goanna. The a simple foreach to iterate over the array. The output looks like this..

  • 0 - koala
  • 1 - kangaroo
  • 2 - Goanna
  • 3 - wallaby
  • 4 - emu
  • 5 - kiwi
  • 1 - kangaroo
  • 2 - Goanna
  • 3 - wallaby
  • 4 - emu
  • 6 - kookaburra
  • 7 - platypus

Notice here how the iterator pointer has been re-wound back to the beginning of the array when we used the offsetUnset() method to unset the array whose key had the value of kiwi. The iteration begins again and as you can see, the key of 5 with the value of kiwi is missing from the object. The internal point can be re-wound intentionally should we wish, using the rewind() method.


<?php
/*** a simple array ***/
$array = array('koala''kangaroo''wombat''wallaby''emu''kiwi''kookaburra''platypus');

try {
  
/*** create a new iterator object ***/
    
$object = new ArrayIterator($array);
  
/*** iterate over the array ***/
  
foreach($object as $key=>$value)
        {
        echo 
$value.'<br />';
        }
  
/*** rewind the internal pointer ***/
  
$object->rewind();
  
/*** echo the current array member ***/
  
echo $object->current();
    }
catch (
Exception $e)
    {
    echo 
$e->getMessage();
    }
?>

The code above will produce a list like this:

koala
kangaroo
wombat
wallaby
emu
kiwi
kookaburra
platypus
koala

The iterator has traversed the array successfully, then we have used the $object->rewind method to rewind the internal pointer back to the beginning of the array and display the value of the current member, which is koala.

If we wanted to get the size of the object, it is a trivial matter of using the count method as follows.


<?php
/*** a simple array ***/
$array = array('koala''kangaroo''wombat''wallaby''emu''kiwi''kookaburra''platypus');

try {
  
/*** create a new iterator object ***/
    
$object = new ArrayIterator($array);
  
/*** get the size of the object ***/
  
echo $object->count();
    }
catch (
Exception $e)
    {
    echo 
$e->getMessage();
    }
?>

The above will return a value of 8 as there are 8 members in our array object.

Recursive Array Iterator

The array iterator above is great for traversing single dimensional arrays. Of course not all arrays are like this, quite often we are presented with a multi-dimensional array. In the bad old days to get around this we might have used 2 foreach() loops, which would have been very costly. To traverse a multi-dimensional array we use the RecursiveArrayIterator(). Lets take a quick at how it looks.

<?php

/*** an array of animal ***/
$animals = array(
    array(
'type'=>'dog''name'=>'butch''sex'=>'m''breed'=>'boxer'),
    array(
'type'=>'dog''name'=>'fido''sex'=>'m''breed'=>'doberman'),
    array(
'type'=>'dog''name'=>'girly','sex'=>'f''breed'=>'poodle'),
    array(
'type'=>'cat''name'=>'tiddles','sex'=>'m''breed'=>'ragdoll'),
    array(
'type'=>'cat''name'=>'tiddles','sex'=>'f''breed'=>'manx'),
    array(
'type'=>'cat''name'=>'tiddles','sex'=>'m''breed'=>'maine coon'),
    array(
'type'=>'horse''name'=>'ed','sex'=>'m''breed'=>'clydesdale'),
    array(
'type'=>'perl_coder''name'=>'shadda','sex'=>'none''breed'=>'mongrel'),
    array(
'type'=>'duck''name'=>'galapogus','sex'=>'m''breed'=>'pekin')
);

/*** create a new recursive array iterator ***/
$iterator =  new RecursiveArrayIterator(new ArrayObject($animals));
/*** traverse the $iterator object ***/
while($iterator->valid())
    {
    echo 
$iterator->key().' -- '.$iterator->current().'<br/>';
    
$iterator->next();
    }
?>

The output from the above code will produce the following..

  • 0 -- Array
  • 1 -- Array
  • 2 -- Array

You might be saying WTF! about now as all we see is an array of arrays. This is because we need to iterate recursively over the recursive iterator object. To do this we need an iterator, that will recursively iterate over an iterator. The RecursiveIteratorIterator is the correct tool for this. Lets adjust our code a little to see how it works.

<?php
$array 
= array(
    array(
'name'=>'butch''sex'=>'m''breed'=>'boxer'),
    array(
'name'=>'fido''sex'=>'m''breed'=>'doberman'),
    array(
'name'=>'girly','sex'=>'f''breed'=>'poodle')
);

foreach(new 
RecursiveIteratorIterator(new RecursiveArrayIterator($array)) as $key=>$value)
    {
    echo 
$key.' -- '.$value.'<br />';
    }
?>

In the above code, the RecursiveIteratorIterator() takes an iterator as its argument, in this instance, a RecursiveArrayIterator(), which in turn takes a multi-dimensional array as its arguement. This is a far more graceful way of traversing a multi-dimensional array than we have previously been used to. The above code will now recursively iterate over the multi-dimensional array, and give us output like this below.

  • name -- butch
  • sex -- m
  • breed -- boxer
  • name -- fido
  • sex -- m
  • breed -- doberman
  • name -- girly
  • sex -- f
  • breed -- poodle

Something useful.

We have seen a few implementations of iterators above, but other than iterating over an aggregate structure, such as an array, what can we do with them? The power of iterators comes in that they will iterate over _any_ aggregate structure or list as mentioned above. Here we will use PDO and SPL to create a HTML table from database results. For those not familiar with PDO it is highly recommended you get aquainted with this database interface. An Introduction to PDO is available to get you started. Lets start with the database, we will use SQLite for this excersize. A SQLite database file is availble with a periodic table of elements that will be used in this tutorial. We will see in the three examples below an increasing degree of complexity when using iterators. Each level of complexity allows better control or extensibility. To start with, we will use

<?php
// check for errors 
error_reporting(E_ALL);

 try {
$dsn = new PDO("sqlite2:/home/kevin/html/periodic.sdb");

// the result only implements Traversable
$stmt $dsn->prepare('SELECT * FROM periodic ORDER BY atomicnumber');

// exceute the query
$stmt->execute();

// the result should be an instance of PDOStatement
// IteratorIterator converts it and after that you can do any iterator operation with it
// The iterator will fetch the results for us.
$it = new IteratorIterator($stmt);

// the iterator object now has 5 arrays within.
// Each array contains a result set from the db query
foreach($it as $row)
    {
    
// create the array object
    
$arrayObj = new ArrayObject($row);
    
// iterate over the array
    
for($iterator $arrayObj->getIterator();
    
// check if valid
    
$iterator->valid();
    
// move to the next array member
    
$iterator->next())
        {
        
// output the key and current array value
        
echo $iterator->current() . '<br />';
        }
    echo 
'<hr />';
    }
 
$dsn