Standard PHP Library and the Iterator Interface - Web Programming for Business - PHP Object-Oriented Programming with Oracle

Web Programming for Business - PHP Object-Oriented Programming with Oracle

6 Standard PHP Library and the Iterator Interface

Overview

Two topics are covered in this chapter – the Standard PHP Library and the Iterator interface. The Standard PHP Library (SPL) is a collection of interfaces and classes that provides efficient data access through sophisticated types of looping for aggregate structures such as arrays, database result sets, XML trees, and directory listings. SPL is based on the Iterator interface, which is an effective mechanism for traversing the elements of an aggregate structure.

Learning Objectives

After completing this chapter, you will gain skills in two areas. First, I will show you how to use selected SPL classes to efficiently iterate over an aggregate structure. Second, I will show you how to use the Iterator interface to better understand the looping process and gain more control over aggregate structure iteration. Skills are enhanced through explanation and code examples. The following objectives summarize the skills the chapter will help you develop:

1. Learn the definition and characteristics of SPL.

2. Learn the definition and characteristics of the Iterator interface.

3. Learn the five methods that the Iterator interface is based upon.

4. Learn how to use selected SPL classes to iterate aggregate structures.

5. Learn how to use the Iterator interface with an array.

6. Learn how to use the Iterator interface with XML.

7. Learn how to use the Iterator interface with a database.

8. Learn the definition and characteristics of the ‘IteratorAggregate’ interface.

9. Learn how to use the ‘IteratorAggregate’ interface.

Definition and Characteristics of SPL

The Standard PHP Library (SPL) is a collection of interfaces and classes that provides efficient data access through sophisticated types of looping for aggregate structures. An aggregate structure is anything you want to loop over. Arrays, database result sets, XML trees, and directory listings are examples of aggregate structures because they hold elements that can be iterated over.

SPL implements the iterator design pattern. The iterator design pattern is an OOP design technique used to iterate over an aggregate structure and access its elements sequentially without exposing its underlying presentation.

The idea is to take the responsibility for access and traversal out of the aggregate structure and place it into an iterator object that defines a standard traversal protocol. A traversal protocol is a set of rules that explains the procedures for iterating an aggregate structure. So, I can place an aggregate structure into an iterator object, and let the iterator object take care of access and traversal of elements in a standard manner. This way, the internal structure is invisible to the outside world. For instance, if I place a database result set into an iterator object, I can manipulate the elements easily without even knowing the internal structure of the database result set! All of the traversal logic is handled by the iterator object.

Definition and Characteristics of the Iterator Interface

The Iterator interface is logically and functionally based on the iterator design pattern. The Iterator interface is a built-in interface that provides the necessary methods to efficiently interact with an iterator. An iterator is an object that provides a means to traverse an aggregate structure. All iterators (including SPL) implement the Iterator interface.

PHP provides two interfaces that allow a programmer to define how objects behave in a ‘foreach’ loop – ‘Iterator’ and ‘IteratorAggregate’. The Iterator interface is discussed in this section. The IteratorAggregate interface is discussed at the end of the chapter.

PHP natively allows a programmer to iterate over objects with a ‘foreach’ loop. However, this process only works with public properties of an object. As such, public properties of the object are the only values that can normally be accessed through ‘foreach’ loop iteration.

Since encapsulation theory advocates using private or protected properties with getters and setters, the properties of the object cannot normally be accessed. The Iterator interface (and ‘IteratorAggregate’) provide a solution because they allow the programmer to define the logic that governs the values to return when an object is iterated over.

Before SPL interfaces and classes can be used with a ‘foreach’ loop, the aggregate structure must be placed into an iterator object. Once it is placed into an iterator object, not only can it be easily traversed with a ‘foreach’ loop; it also has the flexibility of any OOP object, such as inheritance, aggregation, encapsulation, polymorphism, and reflection. This type of flexibility is what OOP is all about!

Five Iterator Interface Methods

The Iterator interface requires the programmer to implement five methods – current, key, next, rewind, and valid. The ‘current()’ method returns the current value in the iteration. The ‘key()’ method returns the key value for the current value. The ‘next()’ method advances the iterator forward one step. The ‘rewind()’ method resets the pointer that is being used for the iteration. The ‘valid()’ method returns a Boolean value that indicates if the value exists in the current position of the iteration.

Selected SPL Classes

Although SPL functionality is based on the Iterator interface, the complexities involved are transparent to the programmer. This is because Iterator interface logic is internal to SPL classes and interfaces. As such, SPL is easy to program because the five Iterator interface methods do not have to be programmatically implemented.

The first SPL class I demonstrate is ‘LimitIterator’, which determines where to start iteration and how many times the loop should run. The first parameter of ‘LimitIterator’ is an object. The second parameter is the starting position (the first element starts at position zero). The third parameter indicates how many elements to include.

PHP file ‘limit.php’ implements ‘LimitIterator’. The program begins by placing an array into an ‘ArrayIterator’ object that is itself wrapped in a ‘LimitIterator’ object limited to two values. The values of the limited object are then displayed.

1 <?php

2 // File limit.php

3 $numbers = array(5,10,8,35,50);

4 $iterator = new ArrayIterator($numbers);

5 $limiter = new LimitIterator($iterator,0,2);

6 foreach($limiter as $number)

7 { echo $number . '<br />'; }

8 ?>

Specifically, I assign the array to ‘$numbers’, which consists of five numeric elements (line 3). I then place the array into an iterator object with ‘ArrayIterator’ and assign it to ‘$iterator’ (line 4). I then wrap ‘$iterator’ in a ‘LimitIterator’ object and assign it to ‘$limiter’ (line 5), which holds the first two elements of the original array. I use ‘foreach’ (lines 6 and 7) to iterate the object in ‘$limiter’ and display the elements in a browser, as shown in Figure 6.1.

Figure 6.1

Figure 6.1 Output Using ‘ArrayIterator’ and ‘LimitIterator’ SPL Classes

I can use the ‘SimpleXMLElement’ class to place an XML document into an iterator object, but it must be wrapped in an SPL iterator. This is because the ‘SimpleXMLElement’ class does not natively implement the Iterator interface.

An elegant way of creating a ‘SimpleXMLIterator’ object is to use it as the second parameter in the ‘simple_load_file()’ method. The next couple of examples use the following XML document, ‘fish.xml’ (all XML documents are available on the companion website):

<?xml version="1.0"?>

<aquarium>

<fish id='9'>

<name>angel fish</name>

<water>fresh</water>

<price>25</price>

</fish>

<fish id='16'>

<name>discus</name>

<water>fresh</water>

<price>18</price>

</fish>

<fish id='76'>

<name>neon tetra</name>

<water>fresh</water>

<price>12</price>

</fish>

<fish id='101'>

<name>tessalata eel</name>

<water>salt</water>

<price>75</price>

</fish>

<fish id='113'>

<name>clown fish</name>

<water>salt</water>

<price>25</price>

</fish>

<fish id='191'>

<name>antennata lion fish</name>

<water>salt</water>

<price>87</price>

</fish>

<fish id='39'>

<name>dinosaur bichir</name>

<water>fresh</water>

<price>110</price>

</fish>

</aquarium>

PHP file ‘fish.php’ uses the ‘SimpleXMLIterator’ class to place ‘fish.xml’ into an iterator object, wraps the object in a ‘LimitIterator’ object, and displays the names.

1 <?php

2 // File fish.php

3 $xml = simplexml_load_file('xml_docs/fish.xml',

4 'SimpleXMLIterator');

5 $limiter = new LimitIterator($xml,2,3);

6 foreach ($limiter as $fish)

7 { echo $fish->name . '<br />'; }

8 ?>

Assuming that ‘fish.xml’ is in the ‘xml_docs’ directory, I place the XML document into an iterator object by adding ‘SimpleXMLIterator’ as the second parameter in the ‘simplexml_load_file()’ method and assigning the object to ‘$xml’ (lines 3 and 4). I then wrap ‘$xml’ in a ‘LimitIterator’ object and assign it to ‘$limiter’ (line 5). The ‘foreach’ loop (lines 6 and 7) iterates ‘$limiter’ and displays the names of fish starting at the third element and continuing for three elements. So, the third, fourth, and fifth fish are displayed. Figure 6.2 shows the results.

Figure 6.2

Figure 6.2 Output Using ‘SimpleXMLIterator’ Wrapper and ‘LimitIterator’ Class

PHP file ‘pcre.php’ applies PCRE to an iterator to refine results with regular expressions. The SPL ‘RegexIterator’ class allows the programmer to use PCRE.

 1 <?php

 2 // File pcre.php

 3 $xml = simplexml_load_file('xml_docs/fish.xml',

 4 'SimpleXMLIterator');

 5 foreach($xml as $fish)

 6 {

 7 $match = new RegexIterator($fish->water,'#fresh#');

 8 foreach($match as $fresh)

 9 {

10 echo $fish->name . ' ' . $fresh;

11 echo ' $' . $fish->price . '.00<br />';

12 }

13 }

14 ?>

I start by placing ‘fish.xml’ into an iterator object (lines 3 and 4). I then traverse the object with a ‘foreach’ loop (lines 5–13). Inside the loop, I wrap all ‘<fish>’ elements that live in fresh water into a ‘RegexIterator’ object and assign to ‘$match’ (line 7). Specifically, the ‘RegexIterator’ object checks if there is a match for string ‘fresh’ in the ‘<water>’ element of each ‘<fish>’ node. The inner ‘foreach’ loop (lines 8–12) traverses ‘$match’ elements and displays results. Figure 6.3 shows the results.

Figure 6.3

Figure 6.3 Output Using ‘SimpleXMLIterator’ and ‘RegexIterator’ SPL Class

The next example uses the SPL ‘AppendIterator’ class to append two iterator objects together and loop through them sequentially in one operation. To illustrate, I use another XML document, ‘more_fish.xml’.

<?xml version="1.0"?>

<aquarium>

<fish id='177'>

<name>cardinal fish</name>

<water>salt</water>

<price>27</price>

</fish>

<fish id='143'>

<name>orbiculate batfish</name>

<water>salt</water>

<price>32</price>

</fish>

<fish id='33'>

<name>bicolor goatfish</name>

<water>fresh</water>

<price>19</price>

</fish>

<fish id='172'>

<name>blue tang</name>

<water>salt</water>

<price>29</price>

</fish>

</aquarium>

PHP file ‘append.php’ appends two XML documents.

 1 <?php

 2 // File append.php

 3 $fish = simplexml_load_file('xml_docs/fish.xml',

 4 'SimpleXMLIterator');

 5 $moreFish = simplexml_load_file('xml_docs/more_fish.xml',

 6 'SimpleXMLIterator');

 7 $combined = new AppendIterator();

 8 $combined->append($fish);

 9 $combined->append($moreFish);

10 echo '<ol>';

11 foreach($combined as $fish)

12 {

13 echo '<li>' . $fish->name . ' ($' . $fish->price . ')';

14 echo ' <' . $fish->water . '></li>'; }

15 echo '</ol>';

16 ?>

Assuming that ‘fish.xml’ and ‘more_fish.xml’ are in the ‘xml_docs’ directory, I place ‘fish.xml’ into an iterator object as ‘$fish’ (lines 3 and 4) and ‘more_fish.xml’ into an iterator object as ‘$moreFish’ (lines 5 and 6). I then create a new instance of ‘AppendIterator’ as ‘$combined’ (line 7). I continue by appending ‘$fish’ and ‘$moreFish’ into ‘$combined’ with method ‘append()’ (line 8 and 9). I finish by iterating ‘$combined’ with a ‘foreach’ loop (lines 11–14) and displaying name, price, and water of each element (lines 13 and 14). Figure 6.4 shows the results.

Figure 6.4

Figure 6.4 Output Using ‘SimpleXMLIterator’ and ‘AppendIterator’ SPL Class

For large documents, ‘LimitIterator’ and ‘AppendIterator’ classes can be used to splice portions of data from multiple documents effectively, limiting what is displayed. PHP file ‘splice.php’ splices limited data from two XML documents and displays results.

 1 <?php

 2 // File splice.php

 3 $fish = simplexml_load_file('xml_docs/fish.xml',

 4 'SimpleXMLIterator');

 5 $moreFish = simplexml_load_file('xml_docs/more_fish.xml',

 6 'SimpleXMLIterator');

 7 $limit1 = new LimitIterator($fish,0,3);

 8 $limit2 = new LimitIterator($moreFish,0,3);

 9 $combined = new AppendIterator();

10 $combined->append($limit1);

11 $combined->append($limit2);

12 echo '<ol>';

13 foreach($combined as $fish)

14 {

15 echo '<li>' . $fish->name . ' ($' . $fish->price . ')';

16 echo ' <' . $fish->water . '></li>'; }

17 echo '</ol>';

18 ?>

I place ‘fish.xml’ into an iterator object as ‘$fish’ (lines 3 and 4) and ‘more_fish.xml’ into an iterator object as ‘$moreFish’ (lines 5 and 6). I then create new instances of ‘LimitIterator’ as ‘$limit1’ and ‘$limit2’ to limit ‘$fish’ and ‘$moreFish’ to three elements each (lines 7 and 8). I continue by appending ‘$limit1’ and ‘$limit2’ with ‘AppendIterator’ and its ‘append()’ method as ‘$combined’ (lines 9–11). I finish by iterating ‘$combined’ with a ‘foreach’ loop (line 13–16) and displaying name, price, and water of each element (lines 15 and 16). Figure 6.5 shows the results.

Figure 6.5

Figure 6.5 Output Using ‘SimpleXMLIterator’ and Various SPL Classes

The ‘CachingIterator’ class allows caching of multiple child elements from a parent element. A cache holds copies of recently accessed data and is designed to speed up processing by prioritizing for quick access. The next example uses this class and XML document ‘composer.xml’.

<?xml version="1.0" encoding="ISO-8859-1"?>

<catalog>

<category>

Small chamber ensembles - 2-4 Players by New York Women Composers

</category>

<composer id="c1">

<name>Julie Mandel</name>

<composition>

<title>Trio for Flute, Viola and Harp</title>

<date><year>1994</year></date>

</composition>

<composition>

<title>Invention for Flute and Piano</title>

<date><year>1994</year></date>

</composition>

</composer>

<composer id="c2">

<name>Margaret De Wys</name>

<composition>

<title>Charmonium</title>

<date><year>1991</year></date>

</composition>

</composer>

<composer id="c3">

<name>Beth Anderson</name>

<composition>

<title>Little Trio</title>

<date><year>1984</year></date>

</composition>

<composition>

<title>Dr. Blood's Mermaid Lullaby</title>

<date><year>1980</year></date>

</composition>

<composition>

<title>Trio: Dream in D</title>

<date><year>1980</year></date>

</composition>

</composer>

<composer id="c4">

<name>Linda Bouchard</name>

<composition>

<title>Propos II</title>

<date><year>1985</year></date>

</composition>

<composition>

<title>Rictus En Miroir</title>

<date><year>1985</year></date>

</composition>

</composer>

</catalog>

PHP file ‘composer.php’ uses the ‘CachingIterator’ class to cache compositions for each composer.

 1 <?php

 2 // File composer.php

 3 $xml = simplexml_load_file('xml_docs/composer.xml',

 4 'SimpleXMLIterator');

 5 echo '<strong>' . $xml->category . '</strong><br />';

 6 echo '<ol>';

 7 foreach($xml->composer as $composer)

 8 {

 9 echo '<li>' . $composer->name . ' => ';

10 $compositions =

11 new CachingIterator($composer->composition);

12 echo '<ul>';

13 foreach($compositions as $comp)

14 {

15 echo '<li>';

16 echo $comp->title . '(' . $comp->date->year . ')';

17 echo '</li>';

18 }

19 echo '</ul></li>';

20 }

21 echo '</ol>';

22 ?>

Assuming ‘composer.xml’ is saved in ‘xml_docs’, I place ‘composer.xml’ into an iterator object as ‘$xml’ (lines 3 and 4). With the first (outer) ‘foreach’ loop, I traverse each composer and display composer info (lines 7–20). I use the ‘CachingIterator’ class to create object ‘$compositions’ that holds compositions for each composer (lines 10 and 11). I use the inner ‘foreach’ loop to traverse each composition by composer and display title and date (lines 13–18). Figure 6.6 shows the results.

Figure 6.6

Figure 6.6 Output Using ‘SimpleXMLIterator’ and ‘CachingIterator’ Class

The Iterator Interface with Arrays

To this point in the chapter, I have demonstrated implicit implementation of the Iterator interface (with SPL). Implicit implementation means that the details of the Iterator interface are transparent to the programmer. That is, the programmer does not have to know anything about how the Iterator interface is implemented.

Now, I will demonstrate explicit implementation of the Iterator interface. Explicit implementation means that the programmer has to know the details of the Iterator interface. That is, the programmer must explicitly use the methods of the Iterator interface to process the aggregate structure. PHP file ‘explicit_array.php’ creates an array and uses explicit iteration.

 1 <?php

 2 // File explicit_array.php

 3 $dude = array('name' => 'David', 'city' => 'London',

 4 'country' => 'United Kingdom');

 5 $iterator = new ArrayIterator($dude);

 6 $iterator->rewind();

 7 while($iterator->valid())

 8 {

 9 echo $iterator->key() . ': ' . $iterator->current() .

10 '<br />';

11 $iterator->next();

12 }

13 $iterator->rewind();

14 echo '<br />' . $iterator->key() . ': ' .

15 $iterator->current();

16 ?>

I begin by placing array ‘$dude’ (lines 3 and 4) into an ‘ArrayIterator’ object as ‘$iterator’ (line 5). I continue by explicitly setting the object to its first element with the ‘rewind()’ method (line 6). I then iterate the object as long as there are elements with the ‘valid()’ method (line 7). Next, I display key and current values with ‘key()’ and ‘current()’ methods respectively (line 9). I use the ‘next()’ method to move to the next element (line 11). After iteration, I reset the object to its first element again (line 13) and display the key and current values (lines 14 and 15). Figure 6.7shows the results.

Figure 6.7

Figure 6.7 Output Using Explicit Array Iteration Methods

Even more control over iteration can be gained by creating a custom Iterator interface class. PHP file ‘array_interface.php’ contains a custom array Iterator interface class. It is purposefully created to work with any array.

 1 <?php

 2 // File array_interface.php

 3 class array_interface implements Iterator

 4 {

 5 private $var = array();

 6 public function __construct($array)

 7 {

 8 if (is_array($array))

 9 { $this->var = $array; }

10 }

11 public function rewind()

12 {

13 echo "rewinding data ... <p />";

14 reset($this->var);

15 }

16 public function current()

17 {

18 $var = current($this->var);

19 return $var;

20 }

21 public function key()

22 {

23 $var = key($this->var);

24 return $var;

25 }

26 public function next()

27 {

28 $var = next($this->var);

29 if($var) { echo " (next: $var)<br />"; }

30 return $var;

31 }

32 public function valid()

33 {

34 $var = $this->current() !== false;

35 return $var;

36 }

37 }

38 ?>

The ‘array_interface’ class (lines 3–37) implements the Iterator interface and includes all five methods – rewind, current, key, next, and valid. The constructor accepts an array (line 6), checks to see if it is actually an array (line 8), and, if so, assigns it to the ‘$var’ property as ‘$this->var’ (line 9). Since the class implements the Iterator interface, the array is automatically placed into an iterator object.

The ‘rewind()’ method (lines 11–15) displays ‘rewinding …’ when invoked (line 13) and resets the array (line 14). The ‘current()’ method (lines 16–20) returns the current element when invoked (lines 18 and 19). The ‘key()’ method (lines 21–25) returns the key when invoked (lines 23 and 24). The ‘next()’ method (lines 26–31) moves the pointer of the iterator object to its next element (line 28), displays the value if available (line 29), and returns the value (line 30) when invoked. The ‘valid()’ method (lines 32–36) checks to see if the current element exists (line 34) and returns it (line 35).

With explicit implementation, the programmer can control what happens in each method of the Iterator interface. PHP file ‘invoke_array_interface.php’ instantiates a new instance of the ‘array_interface’ class.

 1 <?php

 2 // File invoke_array_interface.php

 3 require_once 'array_interface.php';

 4 $values = array("horse","dog","cat");

 5 $it = new array_interface($values);

 6 $it->rewind();

 7 while($it->valid())

 8 {

 9 echo $it->key() . '=> ' . $it->current();

10 $it->next() . '<br />';

11 }

12 echo '<p>';

13 foreach ($it as $a => $b)

14 {

15 echo $a . ' => ' . $b;

16 }

17 ?>

I begin by assigning an array to ‘$values’ (line 4). I continue by creating a new instance of ‘array_interface’ as ‘$it’ (line 5). Next, I rewind object ‘$it’ (line 6). With a ‘while’ loop (lines 7–11), I traverse ‘$it’. I first check if there are any elements left to traverse (line 7) and continue by displaying the key, current, and next values with the object’s methods (lines 9 and 10). Finally, I traverse ‘$it’ with a ‘foreach’ loop (lines 13–16). The ‘foreach’ loop is very convenient because it automatically (implicitly) rewinds and then iterates the object. Figure 6.8 shows the results.

Figure 6.8

Figure 6.8 Output Using a Custom Iterator Interface Class for Arrays

Let’s review. Whether implementing ‘$it’ explicitly (‘while’ loop) or implicitly (‘foreach’ loop), the text ‘rewinding data …’ is displayed first because ‘rewind()’ is the first method invoked (lines 6 and 13). The ‘valid()’ method is then invoked to check if the current element exists (lines 7 and 13). Since the current element exists, the key and element are then displayed by the ‘echo’ statements in the ‘while’ (lines 9 and 10) and ‘foreach’ loops (line 15), respectively. The ‘next()’ method is then invoked to move to the next element (dog) (lines 10 and 13). The ‘valid()’ method is again invoked to check if the current element exists (lines 7 and 13). Since it does, the key and element are again displayed (lines 9 and 10 and line 15). The ‘next()’ method is then invoked to move to the next element (cat) (lines 10 and 13). The ‘valid()’ method is again invoked to check if the current element exists (lines 7 and 13). Since it does, the key and element are again displayed (lines 9 and 10 and line 15). The ‘next()’ method is then invoked to move to the next element (lines 10 and 13). Since it does not exist, ‘next’ is FALSE. When the ‘valid()’ method is invoked (lines 7 and 13), the current element does not exist and the loops terminate.

Line 13 is referenced over and over because ‘foreach’ loops implicitly invoke all of the Iterator interface methods. So, explicit implementation offers added flexibility, but is much more complex. In contrast, implicit implementation is very simple but allows no flexibility to the programmer. Most of the time, implicit implementation is fine but it doesn’t hurt to know how to explicitly use the Iterator interface.

The Iterator Interface with XML

Explicit iteration can also be used to traverse an XML document. The next example uses ‘projects.xml’.

<?xml version = "1.0" encoding="UTF-8" standalone="yes"?>

<document>

<employee badge='1'>

<name>Grace Kelly</name>

<hiredate>October 15, 2005</hiredate>

<projects>

<project>

<product id='111'>Printer</product>

<price>$111.00</price>

</project>

<project>

<product id='222'>Laptop</product>

<price>$989.00</price>

</project>

</projects>

</employee>

<employee badge='2'>

<name>Grant Cary</name>

<hiredate>October 20, 2005</hiredate>

<projects>

<project>

<product id='333'>Desktop</product>

<price>$2995.00</price>

</project>

<project>

<product id='444'>Scanner</product>

<price>$200.00</price>

</project>

</projects>

</employee>

<employee badge='3'>

<name>Clark Gable</name>

<hiredate>October 25, 2005</hiredate>

<projects>

<project>

<product id='555'>Keyboard</product>

<price>$129.00</price>

</project>

<project>

<product id='666'>Mouse</product>

<price>$25.00</price>

</project>

</projects>

</employee>

</document>

PHP file ‘explicit_xml.php’ demonstrates explicit traversal of XML.

 1 <?php

 2 // File explicit_xml.php

 3 $xml = simplexml_load_file('xml_docs/projects.xml',

 4 'SimpleXMLIterator');

 5 $xml->rewind();

 6 while($xml->valid())

 7 {

 8 echo $xml->current()['badge'] . ' ';

 9 echo $xml->current()->name;

10 foreach($xml->current()->projects->project

11 as $project)

12 { echo ' ' . $project->product['id'] . ' ' .

13 $project->product; }

14 echo '<br />';

15 $xml->next();

16 }

17 ?>

Assuming ‘projects.xml’ is in ‘xml_docs’, I place the XML document in an iterator object as ‘$xml’ (lines 3 and 4). I rewind the object (line 5). Next, I use a ‘while’ loop (lines 6–16) to traverse the object. Line 6 checks if there are any elements left to traverse. Lines 8 and 9 display the ‘badge’ and ‘name’ respectively. Since ‘projects’ can have multiple elements, I use a ‘foreach’ loop (lines 10–13) to display the ‘id’ (line 12) and ‘product’ (line 13) values. I move to the next element with ‘next()’ (line 15). Load ‘explicit_xml.php’ in a browser. Figure 6.9 shows the results.

Figure 6.9

Figure 6.9 Output Using ‘SimpleXMLIterator’ and XML Iteration Methods

Even more control over iteration can be gained by creating a custom Iterator interface class. PHP file ‘xml_interface.php’ contains a custom XML Iterator interface class. It is purposefully created to work with any XML document.

 1 <?php

 2 // File xml_interface.php

 3 class xml_interface implements Iterator

 4 {

 5 private $_xml;

 6 private $_position;

 7 public function __construct($xml)

 8 {

 9 $this->_xml = $xml;

10 $this->_position = 0;

11 }

12 public function rewind()

13 { $this->_position = 0; }

14 public function current()

15 {

16 $var = $this->_xml->children()[$this->_position];

17 return $var;

18 }

19 public function key()

20 { $var = $this->_position;

21 return $var; }

22 public function next()

23 { $var = ++$this->_position;

24 return $var; }

25 public function valid()

26 {

27 $count = $this->_xml->count();

28 if($this->_position < $count)

29 { $var = TRUE; }

30 else

31 { $var = FALSE; }

32 return $var;

33 }

34 }

35 ?>

The ‘xml_interface’ class implements the Iterator interface. It includes all five methods – rewind, current, key, next, and valid.

The constructor (lines 7–11) accepts a ‘SimpleXML’ object (line 7). It then sets ‘$this->_xml’ to the iterator object (line 9). It finishes by setting property ‘$this->_position’ to zero (line 10), which is the ‘key’ value because it points to the current element in the iterator object.

The ‘rewind()’ method (lines 12 and 13) accepts no parameters. It sets the ‘key’ value (pointer) to zero (line 13).

The ‘current()’ method (lines 14–18) accepts no parameters. It uses ‘SimpleXML’ method ‘children()’ (line 16) to grab the child element at a given ‘key’ value and returns this value when invoked (line 17).

The ‘key()’ method (lines 19–21) accepts no parameters. It assigns the ‘key’ value to ‘$var’ (line 20) and returns it when invoked (line 21).

The ‘next()’ method (lines 22–24) increments the ‘key’ value of the iterator object to its next element (line 23) and returns it when invoked (line 24).

The ‘valid()’ method (lines 25–33) checks if the current element exists. Specifically, ‘SimpleXML’ method ‘count()’ (line 27) returns the number of elements and is compared against the ‘key’ value (line 28) to determine if the iterator object exists (valid) or does not exist (invalid). The iterator object exists if the ‘key’ is pointing to an element. When the ‘key’ points to nothing, it means that iteration of the object is finished (no elements left to traverse). It sets ‘$var’ to ‘TRUE’ if valid (line 29) or ‘FALSE’ if invalid (line 31).

Although creating a custom Iterator interface is not trivial, it gives the programmer complete control over what happens when iterating an object. You may not use this often, but it is a very powerful tool to have in your programming arsenal.

PHP file ‘invoke_xml_interface.php’ instantiates a new instance of the ‘xml_interface’ class.

 1 <?php

 2 // File invoke_xml_interface.php

 3 require_once 'xml_interface.php';

 4 $xml = simplexml_load_file('xml_docs/projects.xml');

 5 $obj = new xml_interface($xml);

 6 foreach($obj as $value)

 7 {

 8 echo '<strong>' . $value->name . ' ::: </strong>';

 9 foreach($value->projects->project as $datum)

10 {

11 echo '*' . $datum->product['id'] . '* ';

12 echo $datum->product . ' ';

13 echo $datum->price . ' ';

14 }

15 echo '<br />';

16 }

17 ?>

I begin by placing ‘projects.xml’ into a ‘SimpleXML’ object (line 4). I continue by creating a new instance of the ‘xml_interface’ class with the ‘SimpleXML’ object as parameter (line 5). I use an outer ‘foreach’ loop (lines 6–16) to display info of each employee. I use the inner ‘foreach’ loop (lines 9–14) to display information about each project associated with an employee. The inner ‘foreach’ loop is needed because each employee can be associated with more than one project. Load ‘invoke_xml_interface.php’ in a browser. Figure 6.10 shows the results.

Figure 6.10

Figure 6.10 Output Using a Custom Iterator Interface Class for XML

An alternative to the ‘xml_interface’ class is to use the document object model (DOM). DOM is even more flexible, but is more complex to implement. PHP file ‘xml_interface_dom.php’ contains the custom XML Iterator interface class using DOM. It is purposefully created to work with any XML document.

 1 <?php

 2 // File xml_interface_dom.php

 3 class xml_interface_dom implements Iterator

 4 {

 5 private $_xml;

 6 private $_nodeVals;

 7 private $_position;

 8 public function __construct($xml)

 9 {

10 $this->_xml = $xml;

11 $this->_position = 0;

12 $doc = new DOMDocument();

13 $doc->load($xml);

14 $items = $doc->getElementsByTagName('*');

15 $this->_nodeVals =

16 $doc->getElementsByTagName($items->item(1)->nodeName);

17 }

18 public function rewind()

19 { $this->_position = 0; }

20 public function current()

21 {

22 $var = $this->_nodeVals->item($this->_position);

23 return $var;

24 }

25 public function key()

26 {

27 $var = $this->_position;

28 return $var;

29 }

30 public function next()

31 {

32 $var = ++$this->_position;

33 return $var;

34 }

35 public function valid()

36 {

37 $count = $this->_nodeVals->length;

38 if($this->_position < $count)

39 { $var = TRUE; }

40 else

41 { $var = FALSE; }

42 return $var;

43 }

44 }

45 ?>

The ‘xml_interface_dom’ class implements the Iterator interface. It includes all five methods – rewind, current, key, next, and valid.

The constructor (lines 8–17) accepts an XML document (line 8), sets the document to ‘$this->_xml’ (line 10), sets the ‘key’ to ‘$this->_position’ (line 11), creates a new ‘DOMDocument’ object in ‘$doc’ (line12), loads the XML document into DOM (line 13), sets ‘$items’ to hold all XML elements (line 14), and sets ‘$this->_nodeVals’ to hold a value from a given XML element (lines 15 and 16).

The ‘rewind()’ method (lines 18 and 19) accepts no parameters. It sets the ‘key’ to zero (line 19).

The ‘current()’ method (lines 20–24) returns the current element when invoked. The ‘item()’ method (line 22) grabs the element value at a given ‘key’ value. Then, the method returns this ‘key’ value (line 23).

The ‘key()’ method (lines 25–29) returns the key when invoked. The ‘key’ value is assigned to ‘$var’ (line 27) and then returned (line 28).

The ‘next()’ method (lines 30–33) moves the ‘key’ value to the next element when invoked. It increments the ‘key’ (line 32) and returns it (line 33).

The ‘valid()’ method (lines 35–43) checks if the current element exists. The ‘length()’ method (line 37) retrieves the number of elements in the iterator object into ‘$count’. The current ‘key’ is compared to ‘$count’ (line 38). If valid, ‘TRUE’ is assigned to ‘$var’ (line 39). If not, ‘FALSE’ is assigned (line 41). The result is returned (line 42).

PHP file ‘invoke_xml_interface_dom.php’ instantiates a new instance of the ‘xml_interface_dom’ class.

 1 <?php

 2 // File invoke_xml_interface_dom.php

 3 require_once 'xml_interface_dom.php';

 4 $xml = 'xml_docs/projects.xml';

 5 $obj = new xml_interface_dom($xml);

 6 foreach($obj as $value)

 7 {

 8 $name = $value->getElementsByTagName("name");

 9 $nameVal = $name->item(0)->nodeValue;

10 echo '<strong>' . $nameVal . ' ::: </strong>';

11 $searchNode =

12 $value->getElementsByTagName("product");

13 $i = 0;

14 foreach($searchNode as $node)

15 {

16 $id[$i] = $node->getAttribute('id');

17 $i++;

18 }

19 $product = $value->getElementsByTagName("product");

20 $productVal1 = $product->item(0)->nodeValue;

21 $productVal2 = $product->item(1)->nodeValue;

22 $price = $value->getElementsByTagName("price");

23 $priceVal1 = $price->item(0)->nodeValue;

24 $priceVal2 = $price->item(1)->nodeValue;

25 echo '*' . $id[0] . '*' . ' ' .

26 $productVal1 . ' ';

27 echo $priceVal1 . ' ';

28 echo '*' . $id[1] . '*' . ' ' .

29 $productVal2 . ' ';

30 echo $priceVal2;

31 echo '<br />';

32 }

33 ?>

I begin by creating a new DOM instance in ‘$obj’ (line 5). I then use the outer ‘foreach’ loop (lines 6–32) to process iterator object elements.

I retrieve the ‘name’ of each employee is in two stages. First, I use method ‘getElementsByTagName()’ to locate the ‘name’ element and place it in ‘$name’ (line 8). Second, I use method ‘item()’ with ‘nodeValue’ to retrieve the value based on ‘$name’ and place it in ‘$nameVal’ (line 9).

I retrieve product ‘id’ attributes in two stages. First, I use method ‘getElementsByTagName()’ to locate ‘product’ elements and place them in ‘$searchNode’ (lines 11 and 12). Second, I traverse the object in ‘$searchNode’ with an inner ‘foreach()’ loop (lines 14–18). In this loop, I place ‘id’ attributes in array ‘$id’ (lines 16 and 17).

I retrieve the ‘product’ elements in two stages. First, I use method ‘getElementsByTagName()’ to locate ‘product’ elements and place them in ‘$product’ (line 19). Second, I retrieve the name of the first product with ‘$product->item(0)->nodeValue’ (line 20) and the name of the second product with ‘$product->item(1)-> nodeValue’ (line 21).

I retrieve the ‘price’ elements in two stages. First, I use method ‘getElementsByTagName()’ to locate ‘price’ elements and place them in ‘$price’ (line 22). Second, I retrieve the name of the first price with ‘$price->item(0)->nodeValue’ (line 23) and the name of the second price with ‘$price->item(1)->nodeValue’ (line 24). Finally, I display the retrieved data (lines 25–31).

Whew! This one is really complex! This is why I recommend that you use the ‘SimpleXMLElement’ class whenever possible. Load ‘invoke_xml_interface_dom.php’ in a browser. Figure 6.11 shows the results.

Figure 6.11

Figure 6.11 Output Using a Custom DOM Iterator Interface Class for XML

The Iterator Interface with a Database

Using the Iterator interface with a database is more complicated because a database connection (to Oracle in our case) has to be established before there can be any processing. Once a connection is established, SQL and connection information can be sent to the Iterator interface.

I use two classes to demonstrate the Iterator interface with Oracle – ‘dbAggregation’ and ‘dbIterator’. I use the ‘dbAggregation’ class to connect to the Oracle database and send connection information and the SQL statement to the ‘dbIterator’ class. I use the ‘dbIterator’ class to implement the Iterator interface. This class parses and executes the SQL statement, and includes the Iterator interface methods (customized to work with Oracle) that enable iteration with a ‘foreach’ loop.

PHP file ‘dbAggregation.php’ holds the ‘dbAggregation’ class. Be sure to include your username, password, and host server information in the ‘setParms()’ method.

 1 <?php

 2 // File dbAggregation.php

 3 class dbAggregation

 4 {

 5 private $_schema;

 6 private $_password;

 7 private $_host;

 8 protected $_connection;

 9 public function __construct()

10 {

11 $this->setParms();

12 $this->connDB();

13 }

14 public function setParms()

15 {

16 $this->_schema = '';

17 $this->_password = '';

18 $this->_host = '';

19 }

20 public function connDB()

21 {

22 $this->_connection = oci_connect($this->_schema,

23 $this->_password, $this->_host);

24 }

25 public function getResultSet($sql)

26 {

27 $results = new dbIterator($sql,$this->_connection);

28 return $results;

29 }

30 }

31 ?>

The ‘dbAggregation’ class has three private properties and one protected property (lines 5–8). It also contains a constructor and three public methods.

The constructor calls ‘setParms()’ (line 11) and ‘connDB()’ (line 12) methods. It has no parameters.

The ‘setParms()’ method (lines 14–19) sets the Oracle username, password, and host server to their respective private properties. It also has no parameters.

The ‘connDB()’ method (lines 20–24) connects to the database with API ‘oci_connect()’ (lines 22 and 23). It has no parameters.

The ‘getResultSet()’ method (lines 25–29) creates a new instance of the ‘dbIterator’ class with the SQL statement and connection information as parameters (line 27). It then returns the result set to the calling environment (line 28).

Since the ‘dbAggregation’ class creates a new instance of the ‘dbIterator’ class, aggregation is taking place. PHP file ‘dbIteration.php’ holds the ‘dbIteration’ class.

 1 <?php

 2 // File dbIterator.php

 3 class dbIterator implements Iterator

 4 {

 5 public $sql;

 6 public $connection;

 7 protected $_result = array();

 8 protected $_valid;

 9 private $_stmt;

10 public function __construct($sql,$connection)

11 {

12 $this->sql = $sql;

13 $this->connection = $connection;

14 if(!$this->_stmt = oci_parse($connection,$sql))

15 { echo "failed to parse"; };

16 if(!oci_execute($this->_stmt))

17 {echo "failed to execute"; }

18 }

19 public function next()

20 {

21 $this->_result = oci_fetch_assoc($this->_stmt);

22 if(!$this->_result)

23 { $this->_valid = false; }

24 else

25 { $this->_valid = true; }

26 }

27 public function current()

28 {

29 return $this->_result;

30 }

31 public function key()

32 { }

33 public function valid()

34 {

35 return $this->_valid;

36 }

37 public function rewind()

38 {

39 if(!($this->_result))

40 {

41 oci_free_statement($this->_stmt);

42 if(!$this->_stmt = oci_parse($this->connection,

43 $this->sql))

44 { echo "failed to parse"; };

45 if(!oci_execute($this->_stmt))

46 {echo "failed to execute"; }

47 }

48 $this->_result = oci_fetch_assoc($this->_stmt);

49 if(!$this->_result)

50 { $this->_valid = false; }

51 else

52 { $this->_valid = true; }

53 }

54 }

55 ?>

I use the ‘dbIterator’ class to implement the Iterator interface, which converts an SQL result set into an iterator object that can then be traversed with a ‘foreach’ loop.

The class begins by implementing ‘Iterator’ (line 3). The class contains two public properties, two protected properties, and one private property (lines 5–9).

The constructor (lines 10–18) accepts the SQL statement and connection information as parameters (line 10). It then sets the ‘$this->sql’ property to the SQL statement (line 12), sets the ‘$this->connection’ property to the connection information (line 13), parses the SQL (line 14), and executes the SQL (line 16).

The remainder of the class includes five Iterator interface methods. The methods included are next, current, key, rewind, and valid – with appropriate logic for iterating an Oracle object.

The ‘next()’ method uses API ‘oci_fetch_assoc’ to get the next element in the iterator object (lines 19–26). It begins by setting the result to ‘$this->_result’ (line 21). If the next element exists, the ‘$this->_valid’ property is set to ‘true’ (line 25). If not, the ‘$this->_valid’ property is set to ‘false’ (line 23).

The ‘current()’ method (lines 27–30) returns the value of the current element in the object. It accepts no parameters. The ‘key()’ method is not used in Oracle so no logic is included.

The ‘valid()’ method (lines 33–26) returns the ‘$this->_valid’ property. It accepts no parameters.

The ‘rewind()’ method (lines 37–53) checks if there is a result set (line 39). If not, it uses API ‘oci_free_statement’ (line 41) to reset the pointer to the first element in the object (if no result set has been created). It then uses the connection information and the SQL statement as parameters to API ‘oci_parse’ to parse the query (lines 42 and 43). Finally, it uses API ‘oci_execute’ to run the parsed query (line 45). The ‘rewind()’ method continues by using API ‘oci_fetch_assoc’ to fetch the value of the current element (line 48). If data exists, ‘$this->_valid’ is set to ‘true’ (line 52). If not, it is set to ‘false’ (line 50).

In general terms, the ‘dbIterator’ class connects to an Oracle database, uses Oracle APIs to parse, execute, and fetch an Oracle database table result set based on an SQL statement, and uses the Iterator interface methods to traverse an Oracle iterator object.

PHP file ‘implement.php’ uses the ‘dbAggregation’ and ‘dbIterator’ classes to iterate an Oracle database table ‘customers’. This table was created in Chapter 4.

 1 <?php

 2 // File implement.php

 3 require_once 'dbAggregation.php';

 4 require_once 'dbIterator.php';

 5 $tbl = "customers";

 6 $query = "SELECT * FROM $tbl";

 7 $conn = new dbAggregation();

 8 $result = $conn->getResultSet($query);

 9 echo '<p><h2>Iterator Test</h2></p>';

10 foreach($result as $row)

11 {

12 foreach($row as $field => $value)

13 { echo "$field: $value<br />"; }

14 echo '<br />';

15 }

16 ?>

I start by including ‘dbAggregation’ and ‘dbIterator’ classes. I continue by selecting all records from the ‘customers’ table (line 6). I create a new instance of ‘dbAggregation’ as ‘$conn’ (line 7). I use method ‘getResult()’ from ‘$conn’ with ‘$query’ as parameter to get the result set (line 8).

The method creates a new instance of ‘dbIterator’ using aggregation with the SQL statement and connection information as parameters. The new instance of ‘dbIterator’ parses the query, executes the query, and converts the SQL result set into an iterator object. The ‘dbIterator’ iterator object is then assigned to ‘$results’ in the ‘dbAggregation’ object, which is returned to ‘$result’ in the calling environment. Look inside ‘dbIterator.php’ for details.

I traverse each element of the iterator object ‘$result’ with a ‘foreach’ loop (lines 10–15). I use an inner ‘foreach’ loop (lines 12 and 13) because each element of ‘$result’ contains multiple sub-element values. This makes sense because database tables are typically two-dimensional structures consisting of rows and columns.

Load PHP file ‘implement.php’ in a web browser. Figure 6.12 shows a partial view of the results (a total of ten records should be displayed).

Figure 6.12

Figure 6.12 Output Using ‘dbAggregation’ Class and a Database Iterator Interface

Definition and Characteristics of the ‘IteratorAggregate’ Interface

The Iterator interface implements an internal iterator. Internal iterators hide the implementation details of the aggregate structure upon which they work. That is, the Iterator interface automatically handles iteration of the aggregate structure through its internal methods – ‘rewind()’, ‘valid()’, ‘current()’, ‘key()’, and ‘next()’. Details of iterator implementation are hidden.

The ‘IteratorAggregate’ interface creates an external iterator. External iterators are local to the calling environment. That is, all manipulations of external iterators are controlled manually. For instance, ‘next’ won’t be called until you call it! With an internal iterator, ‘next’ (as well as the other methods) is called automatically.

The interface employs the ‘getIterator()’ method to retrieve an external iterator. Method ‘getIterator()’ must have public visibility so the calling environment has access. Upon instantiation of a new object that implements ‘IteratorAggregate’, the ‘getIterator’ method is automatically invoked. As such, an external iterator is automatically returned to the calling environment.

The ‘IteratorAggregate’ Interface

A simple example demonstrates how the ‘IteratorAggregate’ interface works. PHP file ‘array_aggregate.php’ contains the class that implements the interface and PHP file ‘invoke_array_aggregate.php’ contains the code that invokes the class. Notice that the class implements an ‘IteratorAggregate’ rather than an ‘Iterator’ (Iterator interface).

 1 <?php

 2 // File array_aggregate.php

 3 class array_aggregate implements IteratorAggregate

 4 {

 5 private $_wiz;

 6 public function __construct($arr)

 7 {

 8 $this->_wiz = $arr;

 9 }

10 public function getIterator()

11 {

12 return new ArrayIterator($this->_wiz);

13 }

14 }

15 ?>

I use class ‘array_aggregate’ (lines 3–14) to implement the ‘IteratorAggregate’ interface. The constructor accepts an array (line 6) and assigns the array to ‘$this->_wiz’ (line 8). The ‘getIterator()’ method (lines 10–13) places the array into an ‘ArrayIterator’ object that is subsequently wrapped in an ‘IteratorAggregate’ interface object and automatically returned to the calling environment as an external iterator (line 12). The method uses aggregation because it creates a new object inside the method (that is inside the class).

 1 <?php

 2 // File invoke_array_aggregate.php

 3 require_once 'array_aggregate.php';

 4 $list = array('lions','tigers','bears','Oh My!');

 5 $obj = new array_aggregate($list);

 6 foreach($obj as $value)

 7 {

 8 echo $value . '<br />';

 9 echo "\n";

10 }

11 ?>

I use PHP file ‘invoke_array_aggregate.php’ to assign an array to ‘$list’ (line 4). I then create a new instance of ‘array_aggregate’ with ‘$list’ as parameter (line 5). The new object automatically invokes method ‘getIterator()’, which returns an external iterator (based on array ‘$list’) to the calling environment. The external iterator is placed in ‘$obj’. I use a ‘foreach()’ loop to traverse the ‘$obj’ and display results (lines 6–10). Load ‘invoke_array_aggregate.php’ in a browser. Figure 6.13 shows the results.

Figure 6.13

Figure 6.13 Output Using the ‘IteratorAggregate’ Interface for an Array

The ‘IteratorAggregate’ interface can also be used with XML. In the next example, I demonstrate how to place an XML document ‘fish.xml’ (introduced earlier in the chapter) into an external iterator. PHP file ‘xml_aggregate.php’ contains the class and ‘invoke_xml_aggregate.php’ invokes it.

 1 <?php

 2 // File xml_aggregate.php

 3 class xml_aggregate implements IteratorAggregate

 4 {

 5 private $_obj;

 6 public function __construct($obj)

 7 {

 8 $this->_obj = $obj;

 9 }

10 public function getIterator()

11 {

12 return $this->_obj;

13 }

14 }

Class ‘xml_aggregate’ accepts a ‘SimpleXML’ object (line 6), automatically wraps it in an ‘IteratorAggregate’ object, and sets the result to the ‘$this->_obj’ property (line 8). Method ‘getIterator()’ automatically returns an external iterator to the calling environment (lines 10–13).

PHP file ‘invoke_xml_aggregate.php’ places ‘fish.xml’ into a ‘SimpleXML’ object. A new instance of ‘xml_aggregate’ is then created.

 1 <?php

 2 // File invoke_xml_aggregate.php

 3 require_once 'xml_aggregate.php';

 4 $xml = simplexml_load_file('xml_docs/fish.xml');

 5 $obj = new xml_aggregate($xml);

 6 foreach($obj as $value)

 7 {

 8 if($value->water == 'salt')

 9 {

10 echo $value['id'] . ':: ' . $value->name . ' =>';

11 echo ' $' . $value->price . '<br />' . "\n";

12 }

13 }

14 ?>

I place ‘fish.xml’ into a ‘SimpleXML’ object as ‘$xml’ (line 4). I then create a new instance of ‘xml_aggregate’ with ‘$xml’ as parameter (line 5). When I create the new instance, ‘getIterator()’ is automatically invoked. This method returns an external iterator (based on the XML document) to the calling environment and places it in ‘$obj’. I use a ‘foreach()’ loop (lines 6–13) to traverse ‘$obj’ and display results. Load ‘invoke_xml_aggregate.php’ in a browser. Figure 6.14 shows the results.

Figure 6.14

Figure 6.14 Output Using the ‘IteratorAggregate’ Interface for XML

Summary

The goal of this chapter was to promote skill development in two areas. First, code examples of selected SPL classes were presented (with feedback) to promote skills in efficiently iterating a container. Second, Iterator interface examples were presented (with feedback) to promote understanding of the looping process and promote skills in gaining more control over container iteration.

Admittedly, creating custom Iterator interfaces is difficult. However, I truly believe that developing skills in this area is very beneficial for students who want to pursue technical careers in industry.