Home arrow PHP arrow Page 2 - External Iterators in PHP Using OOP

Creating the External Iterator in PHP - PHP

In this first part of a short tutorial series, you will learn how to implement external iterators in PHP using OOP (Object Oriented Programming). This is useful when you want to construct easily traversable classes. Keep reading to learn more!

TABLE OF CONTENTS:
  1. External Iterators in PHP Using OOP
  2. Creating the External Iterator in PHP
  3. Defining the Functionality of PHP Array Iterators
By: Alejandro Gervasio
Rating: starstarstarstarstar / 4
March 21, 2011

print this article
SEARCH DEV SHED

TOOLS YOU CAN USE

advertisement
   
Fixing Things Up: Using an External Iterator

As it was said in the preceding section, it’s necessary to offload to an outer iterator the traversing tasks currently performed by the base “EntityAbstract” class. To do so, the first step that must be taken is to refactor the class in question, whose source code now will look like this:

(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 Utility\ArrayCollection($this->toArray());
    }
   
    /**
     * Count the number of elements in the external iterator
     */
    public function count()
    {
        return count($this->getIterator());
    }   
}

Certainly, a few interesting details are worth noting here, with reference to the refactored version of this abstract class. The first one is that it no longer implements an inner iterator, which is a great improvement toward the application of the Single Responsibility Principle. The second one is that it uses an external iterator, called “ArrayCollection”, which is responsible for traversing the class’ protected members.

Considering that the class now implements the Countable and IteratorAggregate SPL interfaces, the outer iterator will be invoked transparently in a “foreach” loop or when calling explicitly its “getIterator()” method. In a few easy steps, I managed to create a class that employs an external element to traverse its data members.

And speaking of an outer iterator, I still haven’t showcased the definition of the aforementioned “ArrayCollection”. Well, this one is a simple wrapper for a native PHP array and its source code is as follows:
 
(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 it can be seen above, the “ArrayCollection” class is a basic implementation of a PHP array iterator, which also permits to manipulate the elements that it handles using an array-like notation, thanks to the contract imposed by the built-in ArrayAccess interface.



 
 
>>> More PHP Articles          >>> More By Alejandro Gervasio
 

blog comments powered by Disqus
escort Bursa Bursa escort Antalya eskort
   

PHP ARTICLES

- Hackers Compromise PHP Sites to Launch Attac...
- Red Hat, Zend Form OpenShift PaaS Alliance
- PHP IDE News
- BCD, Zend Extend PHP Partnership
- PHP FAQ Highlight
- PHP Creator Didn't Set Out to Create a Langu...
- PHP Trends Revealed in Zend Study
- PHP: Best Methods for Running Scheduled Jobs
- PHP Array Functions: array_change_key_case
- PHP array_combine Function
- PHP array_chunk Function
- PHP Closures as View Helpers: Lazy-Loading F...
- Using PHP Closures as View Helpers
- PHP File and Operating System Program Execut...
- PHP: Effects of Wrapping Code in Class Const...

Developer Shed Affiliates

 


Dev Shed Tutorial Topics: