Custom SPL Iterators

Posted on 20 March 2014
2 minute read

While working on a mobile website for Safelite® AutoGlass, I needed to filter out some objects held within an array. This could have simply been hacked with a basic foreach() loop and some if() / else() statements, but this wouldn’t have resulted in the most reusable code, so I decided to look at the SPL and its many Iterators.

I opted for the RegexIterator and FilterIterator as bases to extend. This allowed me to easily filter objects based on one or more of their properties.

One such example was extracting wipers from a list of available parts. These came in 2 positions, and 2 types: front and rear positions and standard and beam types. I needed to categorise these into types. An example snippet of a wiper XML element looked like:

<Part>
    <Action/>
    <BasePartNumber/>
    <ChildParts/>
    <ClassName>DRIVER FRONT WIPER</ClassName>
    <Description>WIPER BLADE BEAM     22"</Description>
    <DetailTypeCode>BEAM</DetailTypeCode>
    ...
</Part>

From here, we can determine the position of the wiper from the ClassName and the type of wiper by the DetailTypeCode field. The extended iterators were then coded to extract the parts required:

ClassNameIterator

class ClassNameIterator extends \RegexIterator
{
    protected $regex = null;

    public function __construct(\Iterator $iterator, $regex)
    {
        parent::__construct($iterator, $regex);

        $this->regex = $regex;
    }

    public function accept() {
        $item = $this->current();
        return preg_match($this->regex, $item->ClassName);
    }
}

DetailTypeCodeIterator

class DetailTypeCodeIterator extends \FilterIterator
{
    protected $filter;

    public function __construct(\Iterator $iterator, $filter)
    {
        parent::__construct($iterator);
        $this->filter = strtoupper($filter);
    }

    public function accept()
    {
        $item = $this->current();

        if ($this->filter == strtoupper($item->DetailTypeCode)) {
            return $item;
        }
    }
}

Firstly, I needed to create an ArrayIterator from the array of available parts:

$partsDataIterator = new ArrayIterator($partsData->availableParts);

Then it was the case of calling the extended iterators to filter the data:

// Standard front wipers
$classNameIterator = new ClassNameIterator($partsDataIterator, '/.*FRONT WIPER/');
$detailTypeCodeIterator = new DetailTypeCodeIterator($classNameIterator, 'STANDARD');

foreach ($detailTypeCodeIterator as $item) {
    $wipers->standard->front->available = true;
    $wipers->standard->front->totalPrice += $item->ExtendedPrice;
}

// Beam front wipers
$detailTypeCodeIterator = new DetailTypeCodeIterator($classNameIterator, 'BEAM');

foreach ($detailTypeCodeIterator as $item) {
    $wipers->beam->front->available = true;
    $wipers->beam->front->totalPrice += $item->ExtendedPrice;
}

This resulted in a new object with the various wiper types and positions, ready for use within the template.

While the SPL has some real oddities about it, IMO, it also has some great features and classes.