In our second article on the standard PHP library, David Fells explains the new Arrray object, introduces Iterators and the ArrayIterator, and discusses some practical examples of their usage. This article assumes a knowledge of how Exceptions work in PHP 5.
PHP developers are accustomed to using a special variable type - resource handles. Resource handles are used by PHP to identify external resources such as open files, directories and database connections. The introduction of the DirectoryIterator class allows developers to traverse a directory through a standard iterator interface, providing a high level of abstraction between the data source (the directory) and application components that use the data source. Previously a developer would perform a simple directory iteration like this :
$dh = opendir('images/');
if (!$dh)
die('Unable to open directory');
while (($resource = readdir($dh)) !== false) {
print $resource.'<br />';
}
This rudimentary example would traverse a directory and output all the files and directories that it contains. The problem with this method of directory iteration is that the calling application has to know the data source is a directory. The following example demonstrates the use of a directory iterator. After the example, I will explain how this could be used in a non-specific context by the calling object.
This code should look familiar - it is identical to the array example provided above, only with a different Iterator class. You are probably wondering at this point how we have allowed our calling code to stay unaware of the actual data source. Consider this: we have an application that allows a common display component to output the contents of any iterator. The calling application sends it the right iterator object and it outputs the data. This is a highly simplified example, but it is sufficient to demonstrate intent.
try {
TraverseIterator(new DirectoryIterator('images/'));
TraverseIterator(new ArrayIterator (new ArrayObject(array('a', 'b', 'c'))));
}
catch (Exception $e) {
print_r($e);
}
function TraverseIterator(Iterator $iter) {
while ($iter->valid()) {
print $iter->current().'<br />';
$iter->next();
}
}
In this example we define a function called TraverseIterator that takes an Iterator object as its only argument. If an object that is not a child class of Iterator is passed to the function, an Exception will be thrown. Our application calls the function twice - once with a DirectoryIterator and once with an ArrayIterator. There are certainly more practical applications of this technique but this demonstrates the basic idea.
Going further, a developer could create some other object that handles creation of iterators. An object whos job is to create other objects is known as a Factory. If we create a class, IteratorFactory, we can use this to create the iterator needed and the calling application would not have to know what type of iterator to create - it would only need the object to traverse and the type of object. A detailed example of this is a subject for another article, but the concept is worth touching on here.