Using External Iterators to Retrieve Data in PHP

In this final part of our series on external iterators (loops) in PHP, we will look at how to retrieve data using the ArrayIterator SPL class.

In most cases the implementation of external iterators in PHP takes place when it becomes necessary to build wrappers around database result sets and plain arrays. However, they can be of great help when it comes to sticking with the DRY (Don’t Repeat Yourself) and the Single Responsibility principles as well. By using an outer element that encapsulates an intuitive API and all the functionality required for iterating over common data structures like the ones mentioned above, it is possible to build different types of traversable classes without having to pollute them with unnecessary responsibilities.

To demonstrate how to translate these seemingly-complex theoretical concepts into concrete and functional code, in earlier chapters of this series I developed a few examples which showed how to use external iterators; first for traversing the protected fields of an entity modeling class and secondly for constructing a file helper, which had the ability for lazy-loading data from a sample text file. This last example is especially interesting, as its driving logic can be adapted to work with other types of persistence mechanisms, such a RDBMS, a web service or a CDN.

In addition, it’s valid to point out that the aforementioned file helper made use of a custom array collection to fetch (on request) data from a specified target file. Considering that the data was converted to an array prior to being processed, it’d be really easy to refactor the helper and make it work with the native ArrayIterator SPL class. That would mean writing less code, while maintaining the same functionality. A hard-to-beat deal, indeed.

In line with this idea, in this final installment of the series I’m going to show you how simple is to refactor the pertinent file helper, so that it can employ the built-in ArrayIterator class for lazy-loading file data, instead of appealing to a custom array collection.

Recap Time: Lazy-loading File Data Using a Custom External Iterator

In case that you still haven’t had the chance to read the preceding tutorials in this series, I will recap the source code corresponding to this example, so that you can dissect it and grasp its driving logic.

With that being said, here’s an implementation of the pertaining external iterator. Take a look:

(Utility/ArrayCollection.php)

<?php

namespace Utility;

class ArrayCollection implements Countable, Iterator, ArrayAccess
{
    protected $_data = array();
   
    /**
     * Constructor
     */
    public function __construct(array $data = array())
    {
        if (!empty($data)) {
            $this->_data = $data;
        }
        $this->rewind();
    }
   
    /**
     * Count the number of elements in the collection (implementation required by Countable interface)
     */
    public function count()
    {
        return count($this->_data);
    }
   
    /**
     * Reset the collection (implementation required by Iterator interface)
     */    
    public function rewind()
    {
        reset($this->_data);
    }
   
    /**
     * Get the key of the current element in the collection (implementation required by Iterator interface)
     */
    public function key()
    {
        return key($this->_data);
    }
   
    /**
     * Get the current element in the collection (implementation required by Iterator interface)
     */
    public function current()
    {
        return current($this->_data);
    }

    /**
     * Check if there’re more elements in the collection (implementation required by Iterator Interface)
     */
   
    public function valid()
    {
        return ($this->current() !== false);
    }
   
    /**
     * Move to the next element in the collection (implementation required by Iterator interface)
     */
    public function next()
    {
        next($this->_data);
    }
   
    /**
     * Add an element to the collection (implementation required by ArrayAccess interface)
     */
    public function offsetSet($key, $value)
    {
        if (!isset($key)) {
            $this->_data[] = $value;
        }
        else {
            $this->_data[$key] = $value;
        }
        return true;
    }
   
    /**
     * Remove an element from the collection (implementation required by ArrayAccess interface)
     */
    public function offsetUnset($key)
    {
        if (isset($this->_data[$key])) {
            unset($this->_data[$key]);
            return true;
        }
        return false;
    }
   
    /**
     * Get the specified element in the collection (implementation required by ArrayAccess interface)
     */
    public function offsetGet($key)
    {
        return isset($this->_data[$key]) ?
               $this->_data[$key] :
               null;
    } 
   
    /**
     * Check if the specified element exists in the collection (implementation required by ArrayAccess interface)
     */    
    public function offsetExists($key)
    {
        return isset($this->_data[$key]);
    }

    /**
     * Return the elements in the collection
     */
    public function toArray()
    {
        return $this->_data;
    }
}

As you can see, the “ArrayCollection” class is nothing but a generic iterator, which enables it to traverse, count and access the elements stored in its protected $_data field by using an array-like notation.

{mospagebreak title=Save and Fetch Data from Files in PHP}

Considering that the inner workings of this class are extremely easy to follow, move on and pay attention to the following one, which is a simple file helper that saves to - and fetches data from - a specified text file:

(Utility/FileProcessor.php)

<?php

namespace Utility;

class FileProcessor implements IteratorAggregate
{
    protected $_file = ‘Utilitydata.txt’;
    protected $_iterator;
   
    /**
     * Constructor
     */
    public function __construct($file = ”)
    {
        if ($file !== ”) {
            $this->setFile($file);
        }
    }
   
    /**
     * Set the target file
     */
    public function setFile($file)
    {
        if (!file_exists($file)) {
            throw new InvalidArgumentException(‘The target file ‘ . $file . ‘ does not exist.’);
        }
        $this->_file = $file;
    }
   
    /**
     * Get the target file
     */
    public function getFile()
    {
        return $this->_file;
    }
   
    /**
     * Write the specified data to the target file
     */
    public function write($data)
    {
        if (!$fp = fopen($this->_file, ‘a+’)) {
            throw new UnexpectedValueException(‘Error opening the target file.’);
        }
        if (!fwrite($fp, $data . "n")) {
            throw new UnexpectedValueException(‘Error writing data to the target file.’);
        }
        fclose($fp);
        return $this;
    }
   
    /**
     * Get the external iterator
     */
    public function getIterator()
    {
        if ($this->_iterator === null) {
            if (($data = file_get_contents($this->_file)) !== false) {
                $data = explode("n", $data);
                $this->_iterator = new ArrayCollection($data);
            }
            else {
               throw new UnexpectedValueException(‘Error reading data from the target file.’);
            }
        }
        return $this->_iterator;
    } 
}

Regardless of the rather naive implementation of the previous file helper, it’s worth it to take a close look at - and analyze in detail - the implementation of its “getIterator()” method. Effectively, the method invokes internally the earlier array collection in order to retrieve the contents of its target file. But if this method is so simple, what’s so important about it? Well, since the helper implements the “IteratorAggregate” SPL interface, it permits us to transparently lazy-load file data!

If you still aren’t convinced, look at the following snippet, which will should make your doubts vanish in a snap: 

<?php

use UtilityFileProcessor as FileProcessor;

// include the autoloader
require_once ‘Autoloader.php’;
Autoloader::getInstance();

// create an instance of the FileProcessor class
$fileProc = new FileProcessor;

// write some data to the target file
$fileProc->write(‘This is the first line of the file’)
         ->write(‘This is the second line of the file’)
         ->write(‘This is the third line of the file’);

// lazy-load file data
foreach ($fileProc as $key => $value) {
    echo $value . ‘<br />’;
}

/* displays the following

This is the first line of the file
This is the second line of the file
This is the third line of the file

*/

As the above code sample shows, using the file helper for reading/writing data to the specified target text file is actually a breeze. But by far the most engaging aspect of this class is its ability to fetch that data whenever an instance of it is placed inside a “foreach” construct.

Even when quite simplistic, this example shows in a nutshell how the use of an external iterator along with an implementer of the “IteratorAggregate” native interface can compose a hard-to-beat duo, especially when it comes to loading data on request, either from a file, a relational database or a web service.

But before you launch your code editor and start testing the file helper, let me tell you a some more good news. Since the data that it fetches from the target file is converted to an array before being retuned to the client code, it’s possible to improve its implementation by using the built-in ArrayIterator class, instead of appealing to a custom array collection.

Well, that improvement process deserves a close look, right? Therefore, in the following segment I’ll be refactoring the helper so it can use the aforementioned native class.

{mospagebreak title=PHP ArrayIterator SPL Class}

Refactoring the File Helper: Using the Native ArrayIterator SPL Class

To be frank, refactoring the prior file helper so that it can use the SPL ArrayIterator class instead of a custom iterator is a straightforward process reduced to amending its “getIterator()” method and nothing more. If this isn’t clear enough for you, pay attention to the following code sample, which shows the improved version of the helper:

(Utility/FileProcessor.php)

<?php

namespace Utility;

class FileProcessor implements IteratorAggregate
{
    protected $_file = ‘Utilitydata.txt’;
    protected $_iterator;

    /**
     * Constructor
     */
    public function __construct($file = ”)
    {
        if ($file !== ”) {
            $this->setFile($file);
        }
    }

    /**
     * Set the target file
     */
    public function setFile($file)
    {
        if (!file_exists($file)) {
            throw new InvalidArgumentException(‘The target file ‘ . $file . ‘ does not exist.’);
        }
        $this->_file = $file;
    }

    /**
     * Get the target file
     */
    public function getFile()
    {
        return $this->_file;
    }

    /**
     * Write the specified data to the target file
     */
    public function write($data)
    {
        if (!$fp = fopen($this->_file, ‘a+’)) {
            throw new UnexpectedValueException(‘Error opening the target file.’);
        }
        if (!fwrite($fp, $data . "n")) {
            throw new UnexpectedValueException(‘Error writing data to the target file.’);
        }
        fclose($fp);
        return $this;
    }

    /**
     * Get the external iterator
     */
    public function getIterator()
    {
        if ($this->_iterator === null) {
            if (($data = file_get_contents($this->_file)) !== false) {
               $data = explode("n", $data);
               $this->_iterator = new ArrayIterator($data);
            }
            else {
                throw new UnexpectedValueException(‘Error reading data from the target file.’); 
            }
        }
        return $this->_iterator;
    }
}

Mission accomplished, at least for the moment. As you can see, the file helper is still capable of lazy-loading data from the target file, only now it performs this task via the native ArrayIterator class. With this subtle, yet useful change already implemented, the only thing that needs to be done is set up an example that shows the functionality of the file helper in its “renewed” state.

Setting Up a Final Example: Putting the File Helper to Work

As you may already guessed, testing the revamped version of the file helper doesn’t require us to alter any section of the client code that consumes it, as the substitution of a custom external iterator for a native one has been neatly shielded from the outside.

The following script shows this in a nutshell. Check it out: 

<?php

use UtilityFileProcessor as FileProcessor;

// include the autoloader
require_once ‘Autoloader.php’;
Autoloader::getInstance();

// create an instance of the FileProcessor class
$fileProc = new FileProcessor;

// write some data to the target file

$fileProc->write(‘This is the first line of the file’)
         ->write(‘This is the second line of the file’)
         ->write(‘This is the third line of the file’);

// lazy-load file data
foreach ($fileProc as $key => $value) {
    echo $value . ‘<br />’;
}

/* displays the following

This is the first line of the file
This is the second line of the file
This is the third line of the file

*/

Done. At this point, I managed to create a file helper capable of lazy-loading data, which performs this task thanks to the use of a built-in external iterator. Of course, in the real world things are not so easy as they are in the above example, which means that in many cases it’s necessary to implement a custom iterator.

Nevertheless, if you’re planning to build an application that, among other things, will traverse, count and access a lot of arrays, the native ArrayIterator class encapsulates enough functionality to get the job nicely done. So, keep your eyes on it in your next PHP project.     

Final Thoughts

Finally, we’ve come to the end of this hopefully educational journey on building external iterators in PHP. Armed with the examples that I provided you in the subsequent tutorials, at this stage you should be capable of building your own external iterators with minor hassles. And bear in mind that, no matter if you need to iterate over database result sets, class data members or simply plain vanilla arrays, external iterators are useful structures that will let you build classes that have a fewer range of responsibilities.

See you in the next PHP tutorial!

 

[gp-comments width="770" linklove="off" ]
antalya escort bayan antalya escort bayan