External PHP Iterators using the ArrayIterator Class

If you, as a PHP developer, are looking for an approach that lets you build traversable classes that stick more strictly to the Single Responsibility Principle, then maybe it is time to consider the functionality provided by external iterators.

To demonstrate how useful external iterators can be, in the introductory part of this series I implemented a custom array collection, which was used by a separate class, whose main area of concern was to model generic entities. The purpose of coding this array wrapper was to traverse the protected fields of the mentioned class without having to use an inner iterator. A dead simple solution, but also effective. You can read more in my article: External Iterators in PHP Using OOP.

It’s worth noting that it is not always necessary to create custom external iterators from scratch. If the data that must be traversed is made up of plain array elements, it’s possible to use the ArrayIterator class that comes included with the SPL. Taking into account this possibility, in this second installment of the series I’ll be illustrating how to use this built-in iterator, this time to traverse the data members of the entity modeling class previously created.

Want to learn the fine details of this refactoring process? Then jump ahead and read on!

Using a Custom External Iterator: A Brief Look at a Prior Example

Before I show you how easy it is to build traversable objects using the native PHP ArrayIterator class,   it would be useful to take a quick look at the example created in the first installment of this series, which as mentioned in the introduction, demonstrated how to traverse the protected fields of an entity class via a custom external iterator.

With that being said, here’s the abstract class that models generic entities and , in turn, uses an outer array collection to iterate over the properties of any subclass derived from it:

(Model/EntityAbstract.php)

<?php

namespace Model;
use Utility;

abstract class EntityAbstract implements Countable, IteratorAggregate
{
    protected $_allowedFields = array();
   
    /**
     * Constructor
     */
    public function __construct(array $fields)
    {
        foreach ($fields as $name => $value) {
            $this->$name = $value;
        }
    }
   
    /**
     * Set a value for the specified field (via the mutator if it exists or by assigning it to the field)
     */
    public function __set($name, $value)
    {
        if (!in_array($name, $this->_allowedFields)) {
            throw new OutOfRangeException(‘Setting the field ‘ . $name . ‘ is not allowed for this entity.’);
        }
        $mutator = ‘set’ . ucfirst(str_replace(‘_’, ”, $name));
        if (method_exists($this, $mutator) && is_callable(array($this, $mutator))) {
            $this->$mutator($value);          
        }
        else if(property_exists($this, $name)) {
            $this->$name = $value;  
        }
        else {
            throw new InvalidArgumentException(‘The field ‘ . $name . ‘ does not exist for this entity.’);
        }
    }
   
    /**
     * Get the value of the specified field (via the getter if it exists or by getting it from the field)
     */
    public function __get($name)
    {
        if (!in_array($name, $this->_allowedFields)) {
            throw new OutOfRangeException(‘Getting the field ‘ . $name . ‘ is not allowed for this entity.’);
        }
        $accessor = ‘get’ . ucfirst(str_replace(‘_’, ”, $name));
        if (method_exists($this, $accessor) && is_callable(array($this, $accessor))) {
            return $this->$accessor;   
        }
        if (property_exists($this, $name)) {
            return $this->$name;
        }
        throw new InvalidArgumentException(‘The field ‘ . $name . ‘ does not exist for this entity.’);   
    }
   
    /**
     * Passthrough for setting/getting field values
     */
    public function __call($name, $arguments)
    {
        if (strlen($name) > 3) {
            $property = strtolower(substr($name, 3));
            if (property_exists($this, $property)) {
                if (strpos($name, ‘set’) === 0) {
                    $this->$property = array_shift($arguments);
                    return $this;
                }
                if (strpos($name, ‘get’) === 0) {
                    return $this->$property;
                }
            }
        }
        throw new BadMethodCallException(‘The mutator/accessor ‘ . $name . ‘ does not exist for this entity.’);
    }
         
    /**
     * Create an array with the field/value pairs of the entity
     */
    public function toArray()
    {
        $fields = get_object_vars(($this));
        unset($fields['_allowedFields']);
        return $fields;
    }
   
    /**
     * Get the external iterator
     */
    public function getIterator()
    {
        return new UtilityArrayCollection($this->toArray());
    }
   
    /**
     * Count the number of elements in the external iterator
     */
    public function count()
    {
        return count($this->getIterator());
    }   
}

As you can see, aside from setting/retrieving the values assigned to the fields of an entity, the above class implements the Countable and IteratorAggregate SPL interfaces, in order to count and iterate over the fields in question via an outer array collection. If you’re wondering how this collection looks, here is its source code:

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

If you are used to building wrappers around PHP native arrays, the implementation of the above “ArrayCollection” class should be quite easy to grasp for you. In simple terms, all this class does is traverse the elements added to its protected $_data array and access them via the methods declared in the ArrayAccess SPL interface.

And now that you have grasped how the previous iterator works, it’s time to spawn a concrete subclass from the parent “EntityAbstract”, so that you can see how it can be easily traversed using the pertaining iterator. In this particular case, this subclass is responsible for modeling generic users and looks like this: 

(Model/User.php)

<?php

namespace Model;

class User extends EntityAbstract
{
    protected $_allowedFields = array(‘_id’, ‘_name’ , ‘_email’);
    protected $_id;
    protected $_name;
    protected $_email;  
   
    /**
     * Set the user’s ID
     */
    public function setId($id)
    {
        if(!filter_var($id, FILTER_VALIDATE_INT, array(‘options’ => array(‘min_range’ => 1, ‘max_range’ => 65535)))) {
            throw new OutOfRangeException(‘The supplied ID ‘ . $id . ‘ is invalid.’);
        }
        $this->_id = $id;
    }
     
    /**
     * Set the user’s name
     */ 
    public function setName($name)
    {
        if (strlen($name) < 2 || strlen($name) > 32) {
            throw new OutOfRangeException(‘The supplied name ‘ . $name . ‘ is invalid.’);
        }
        $this->_name = $name;
    }
     
    /**
     * Set the user’s email address
     */
    public function setEmail($email)
    {
        if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
            throw new OutOfRangeException(‘The supplied email address ‘ . $email . ‘ is invalid.’);
        }
        $this->_email = $email;
    }
}

The above “User” class’ source code speaks for itself, so move on and look at the following script, which shows how to iterate over the class’ properties using the earlier “ArrayCollection” iterator.

{mospagebreak title=Iterate Class Properties with ArrayCollection}

<?php

use ModelUser as User;

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

// create a user object
$user = new User(array(
    ‘_id’    => 1,
    ‘_name’  => ‘Sandra Wilson’,
    ‘_email’ =>
‘sandra@domain.com’
 ));

// count the number of fields of the user
echo ‘The number of fields of the user is ‘ . count($user) . ‘.<br />';

/* displays the following
The number of fields of the user is 3.
*/

// iterate over the user and display their value/field pairs
foreach ($user as $field => $value) {
    echo "The value assigned to the field ‘$field’ is $value.<br />";
}

/* displays the following
The value assigned to the field ‘_id’ is 1.
The value assigned to the field ‘_name’ is Sandra Wilson.
The value assigned to the field ‘_email’ is
sandra@domain.com.
*/

That was really simple to code and read, wasn’t it? As seen in the above code fragment, traversing any object spawned from the “User” class is a breeze, thanks to the functionality provided by the custom array collection. As noted in the beginning, however, it’s possible to get similar results by using the native ArrayIterator class included with the SPL.

To elaborate this concept, in the following segment I’m going to modify the implementation of the prior “EntityAbstract” class, which will make use of the aforementioned ArrayIterator to traverse the protected data members of any subclass spawned from it.

Tweaking the Earlier Example: Using the Native ArrayIterator Class 

Modifying the previous “EntityAbstract” class so that it can use the native ArrayIterator SPL class is a straightforward process reduced to refactoring its “getIterator()” and “count()” methods and nothing else. Of course, this is pure theory that needs to be backed up with functional code, so pay close attention to the following snippet, which shows the modified version of this class:

(Model/EntityAbstract.php)

<?php

namespace Model;

abstract class EntityAbstract implements Countable, IteratorAggregate
{
    protected $_allowedFields = array();

    /**
     * Constructor
     */
    public function __construct(array $fields)
    {
        foreach ($fields as $name => $value) {
            $this->$name = $value;
        }
    }

    /**
     * Set a value for the specified field (via the mutator if it exists or by assigning it to the field)
     */
    public function __set($name, $value)
    {
        if (!in_array($name, $this->_allowedFields)) {
            throw new OutOfRangeException(‘Setting the field ‘ . $name . ‘ is not allowed for this entity.’);
        }
        $mutator = ‘set’ . ucfirst(str_replace(‘_’, ”, $name));
        if (method_exists($this, $mutator) && is_callable(array($this, $mutator))) {
            $this->$mutator($value);
        }
        else if(property_exists($this, $name)) {
            $this->$name = $value;
        }
        else {
            throw new InvalidArgumentException(‘The field ‘ . $name . ‘ does not exist for this entity.’);
        }
    }

    /**
     * Get the value of the specified field (via the getter if it exists or by getting it from the field)
     */
    public function __get($name)
    {
        if (!in_array($name, $this->_allowedFields)) {
            throw new OutOfRangeException(‘Getting the field ‘ . $name . ‘ is not allowed for this entity.’);
        }
        $accessor = ‘get’ . ucfirst(str_replace(‘_’, ”, $name));
        if (method_exists($this, $accessor) && is_callable(array($this, $accessor))) {
            return $this->$accessor;
        }
        if (property_exists($this, $name)) {
            return $this->$name;
        }
        throw new InvalidArgumentException(‘The field ‘ . $name . ‘ does not exist for this entity.’);
    }

    /**
     * Passthrough for setting/getting field values
     */
    public function __call($name, $arguments)
    {
        if (strlen($name) > 3) {
            $property = strtolower(substr($name, 3));
            if (property_exists($this, $property)) {
                if (strpos($name, ‘set’) === 0) {
                    $this->$property = array_shift($arguments);
                    return $this;
                }
                if (strpos($name, ‘get’) === 0) {
                    return $this->$property;
                }
            }
        }
        throw new BadMethodCallException(‘The mutator/accessor ‘ . $name . ‘ does not exist for this entity.’);
    }

    /**
     * Create an array with the field/value pairs of the entity
     */
    public function toArray()
    {
        $fields = get_object_vars(($this));
        unset($fields['_allowedFields']);
        return $fields;
    }

    /**
     * Get the external iterator
     */
    public function getIterator()
    {
        return new ArrayIterator($this->toArray());
    }

    /**
     * Count the number of elements in the external iterator
     */
    public function count()
    {
        return count($this->getIterator());
    }


  
Effectively, the implementation of the above class remains nearly the same, except for a couple of subtle details: as mentioned, the “getIterator()” and “count()” methods now use (internally) the native ArrayIterator class as an external iterator, instead of the custom array collection previously created.

   Effectively, the implementation of the above class remains nearly the same, except for a couple of subtle details: as mentioned, the “getIterator()” and “count()” methods now use (internally) the native ArrayIterator class as an external iterator, instead of the custom array collection previously created.

Since ArrayIterator on its own packages the functionality required for traversing, counting and handling plain PHP arrays, in this case it can be used for traversing the properties of any entity derived from the earlier abstract parent.

Moreover, to demonstrate the veracity of my claim, in the following segment I’ll be setting up a short script, similar to others that you learned before in this series, which will make use of the amended version of the “EntityAbstract” class, to iterate over the fields of a user object. 

Traversing User Objects: Seeing the ArrayIterator in Action

If you’re anything like me, you’ll want to see if the revamped “EntityAbstract” class is really iteratable. To satisfy your curiosity, I’m going to use the same “User” subclass that you saw in the earlier segment. As you’ll recall, this derivative is responsible for modeling users and its implementation is as follows:

(Model/User.php)

<?php

namespace Model;

class User extends EntityAbstract
{
    protected $_allowedFields = array(‘_id’, ‘_name’ , ‘_email’);
    protected $_id;
    protected $_name;
    protected $_email;

    /**
     * Set the user’s ID
     */
    public function setId($id)
    {
        if(!filter_var($id, FILTER_VALIDATE_INT, array(‘options’ => array(‘min_range’ => 1, ‘max_range’ => 65535)))) {
            throw new OutOfRangeException(‘The ID ‘ . $id . ‘ is invalid.’);
        }
        $this->_id = $id;
    }

    /**
     * Set the user’s name
     */
    public function setName($name)
    {
        if (strlen($name) < 2 || strlen($name) > 32) {
            throw new OutOfRangeException(‘The name ‘ . $name . ‘ is invalid.’);
        }
        $this->_name = $name;
    }

    /**
     * Set the user’s email address
     */
    public function setEmail($email)
    {
        if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
            throw new OutOfRangeException(‘The email address ‘ . $email . ‘ is invalid.’);
        }
        $this->_email = $email;
    }
}

Admittedly, I included the above class only for the sake of completeness, but at this point you should already be familiar with its inner workings. So, it’s time to make things a bit more interesting and see if the properties of any user object can be actually traversed. 

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

<?php

use ModelUser as User;

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

// create a user object
$user = new User(array(
    ‘_id’    => 1,
    ‘_name’  => ‘Sandra Wilson’,
    ‘_email’ =>
‘sandra@domain.com’
));

// count the number of fields of the user
echo ‘The number of fields of the user is ‘ . count($user) . ‘.<br />';

/* displays the following
The number of fields of the user is 3.
*/

// iterate over the user and display their value/field pairs
foreach ($user as $field => $value) {
    echo "The value assigned to the field ‘$field’ is $value.<br />";
}

/* displays the following
The value assigned to the field ‘_id’ is 1.
The value assigned to the field ‘_name’ is Sandra Wilson.
The value assigned to the field ‘_email’ is
sandra@domain.com.
*/

That ran really smooth! Effectively, any object originated from the “User” class (and in turn derived from its abstract parent) can be placed within a “foreach” loop and traversed at will. And best of all, the whole process didn’t require us to write a custom iterator, as the native ArrayIterator class did the hard work for us.

Even when somewhat contrived, this example showed that when it comes to using external iterators, some of the SPL classes can save you from reinventing the wheel (unless you really need to have better and flashier wheels).

But wait a minute! Does this mean that the functionality provided by external iterators is reduced to traversing a few entities and nothing else? Fear not, as they can be utilized in a great variety of use cases. But to learn more on this topic, feel free to take a look at the tutorial’s closing thoughts.

Final thoughts

Over this second chapter of the series, I demonstrated with yet another approachable example, how easy is to create traversable classes by using an external iterator. In this specific case, I decided to employ the native ArrayIterator class that comes bundled with the SPL to traverse the protected fields of an entity class; but as you saw in a previous tutorial, it’s possible (and in most cases desirable) to implement a custom iterator, capable of performing additional tasks, other than the ones provided by the classic tandem composed by the Iterator/Countable/ArrayAccess SPL interfaces.

While it’s fair to admit that this isn’t an exclusive ability of external iterators, the truth is that it’s feasible to use them for lazy-loading data as well. You may be wondering how this can be done, right? Considering that an outer iterator is usually invoked by a class implementing the “IteratorAggregate” native interface, it’s really simple to load data on request, since its “getIterator()” method is invoked whenever any object of the class is used within a “foreach” construct.

Based on this concept, in the coming tutorial I’m going to create a simple file helper, which will be able to lazy-load contents from a specific text file, thanks to the “clever” use of an external iterator.

Don’t miss the next part!

 

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

antalya escort bayan antalya escort bayan Antalya escort diyarbakir escort