Lazy Loading with External PHP Iterators

Let’s face it: building custom external iterators is one of those things that we, as PHP developers, have to tackle sooner or later. Whether it comes to iterating over database result sets or traversing plain array elements, external iterators are powerful, reusable structures that permit you to develop more efficient object-oriented applications. This is the third part in our series on external iterators in PHP.

The fun side of outer iterators (at least when using an OOP approach) is that their implementation is (in most cases) a straightforward process that can be mastered in a snap. Of course, claming this without  concrete proof would be rather pointless. In line with this idea, in previous installments of this series I went through the development of a couple of concrete examples that demonstrated how to use an external iterator for traversing the protected fields of a class whose primary use was to model generic entities.

In the first tutorial, the whole traversing task was performed via a custom array collection, while the second how-to achieved the same process through the native ArrayIterator class bundled with the SPL. In both cases, the results were nearly identical, even though the use of a built-in class like ArrayIterator, saved us from the hassles of having to write an array wrapper all by ourselves. You find both tutorials here: External Iterators in PHP Using OOP and External PHP Iterators using the ArrayIterator Class respectively.

It goes without saying that external iterators can be used for many different purposes, aside from those described above. What’s more, they can be utilized not only for traversing structures, but for lazy-loading data as well.

If you’re wondering how this can be achieved, I’ll be building a simple file helper class, which will be responsible for reading/writing data to a specified text file in the following sections. The “beauty” of this helper is that it will be able to lazy-load the data written to the pertaining file through the same array collection that you saw in the preceding tutorials.

Traversing Array Elements: Building a Custom Array Collection    

As stated in the introduction, the file helper that I plan to build in the following sections will use an external iterator to lazy-load the data saved to a given text file. In this particular case, I’ll be somewhat lazy too since the iterator employed by the helper in question will be the same custom array collection implemented in earlier chapters of this series.

In case that this collection doesn’t ring any bells to you, here it is again for your perusal:

(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;
    }
}

Pretty simple right? As seen above, the “ArrayCollection” class is nothing but a simple wrapper for plain PHP arrays, which permits us to iterate over its elements, count them, and set/unset them as well.

In the following section I’ll be building the aforementioned file helper class, which will use the earlier array collection for lazy-loading the data written to a specific file.

{mospagebreak title=Building File Helpers in PHP}

Using the Previous Array Collection: Building a Basic File Helper

As we saw in the previous section, the file helper that I plan to create will make use of the earlier array collection to iterate over the contents of a specified text file. Since I want the collection to be invoked only when the helper is consumed within a “foreach” loop, the latter will be an implementer of the native “IteratorAggregate” SPL interface.

If this speech sounds somewhat confusing to you, don’t worry too much – take a look at the following code snippet, which includes the source code 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 ArrayCollection($data);
            }
            else {
               throw new UnexpectedValueException(‘Error reading data from the target file.’);
            }
        }
        return $this->_iterator;
    } 
}

Despite its fancy name, the area of concern for the “FileProcessor” class is quite limited, as it’s only responsible for reading and writing text data to a specified target file. Even when the implementation of the class’ “write()” method is dead simple, you should pay attention to its counterpart “getIterator()”, which is worth analyzing in more depth.

Effectively, the class uses this method to read data from the target file and inject it into the custom array collection, but only when the method is called explicitly or when invoked transparently within a “foreach” construct. This shows – in a nutshell – how an external iterator (in this case a custom array collection) can be used for lazy-loading data, no matter if it comes from a flat database, an RDBMS, or a web service. Pretty good, huh?

And now that you grasp the logic that stands behind the previous file helper, it’s time to give it a try and see if it’s as functional as it seems at first sight.

Putting the Pieces Together: Seeing the File Helper in Action

If you want to see the previous file helper in action, below is a short script that uses it firstly for saving some trivial text to the default “data.txt” file, and then for getting this data back. As just stated, this last operation is performed only when an instance of the helper is embedded into a “foreach” loop.

Here’s how this test script looks (remember that the autoloader is the same used here [http://www.devshed.com/c/a/PHP/Building-a-service-Locator-in-PHP/]):

<?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

*/

There you have it. In a few easy steps, I managed to create a file helper, which is not only capable of lazy-loading data through the implementation of the IteratorAggregate SPL interface, but performs this task via a custom external iterator.

In this case the helper isn’t really important (in fact, I’m sure that you can build a more functional one on your own). What’s really worth noting here is how it uses an array collection for fetching data on request, which is then traversed using an array collection. This approach can be easily translated to other storage mechanisms, such as a database or a CDN, and proves particularly useful when it comes to manipulating common relationships of data (one-to-one/one-to-many) in a more efficient way.

So, fire up your spark of creativity and start using the functionality of external iterators for lazy-loading data in your own applications. The results that you’ll get will put a big smile on your face, believe me.       

Final thoughts

In this third episode of the series, I demonstrated with a concrete example how the use of a custom external iterator can be of great help when it comes to lazy-loading data from a specific storage mechanism, being in this case the file system. In the above example, the implementation of a basic file helper was all that I needed to made my point, but this approach can be easily extended to other contexts as well (i.e. when working with a RDBMS).

What’s more, if you’re a sharp observer, you’ll probably have realized that the previous helper can be tweaked to work seamlessly with the native ArrayIterator SPL class instead of using a custom array collection. Well, that’s exactly what I plan to do in the final tutorial of the series, so don’t miss it!

[gp-comments width="770" linklove="off" ]

antalya escort bayan antalya escort bayan Antalya escort diyarbakir escort