Painless Data Traversal with PHP FilterIterator

There’s load of ways to traverse data, especially in PHP where there are a variety of loops available; including while, do while, for and foreach. These are fine for normal structures, such as scalar and associative arrays. But what if you want to get a bit more fancy?

Say you started out with a structure similar to the following:

<?php
$dataList = array(
    array(
        'name' => 'John Citizen',
        'email' => 'johnc@citizen.org',
        'dob' => 1350910989,
        'location' => 'Brisbane, Qld, Australia',
        'active' => true
    ),
    array(
        'name' => 'Jane Citizen',
        'email' => 'janec@citizen.org',
        'dob' => 1350910989,
        'location' => 'Townsville, Qld, Australia',
        'active' => false
    ),
    array(
        'name' => 'Peter Walker',
        'email' => 'peterw@citizen.org',
        'dob' => 1350910989,
        'location' => 'Sydney, Nsw, Australia',
        'active' => false
    ),
    array(
        'name' => 'Wendy Hardworker',
        'email' => 'wendyh@citizen.org',
        'dob' => 1350910989,
        'location' => 'Melbourne, Vic, Australia',
        'active' => true
    ),
    array(
        // ...
    ),
);

You can see that you could quickly and simple filter the information using any of the previously mentioned loops. The following is a rather trivial example of how you could do just this.

<?php
foreach ($dataList as $userDetail) {
    printf(
        "Name: %s | Email: %s | Date Of Birth: %s | Location: %s",
        $userDetail["name"], 
        $userDetail["email"], 
        $userDetail["dob"], 
        $userDetail["location"]
    );
}

This would display output similar to the following:

Name: John Citizen | Email: johnc@citizen.org | DOB: 1350910989 | Location: Brisbane, Qld, Australia
Name: Jane Citizen | Email: janec@citizen.org | DOB: 1350910989 | Location: Townsville, Qld, Australia
Name: Peter Walker | Email: peterw@citizen.org | DOB: 1350910989 | Location: Sydney, Nsw, Australia
Name: Wendy Hardworker | Email: wendyh@citizen.org | DOB: 1350910989 | Location: Melbourne, Vic, Australia

But say, over time, the the data structure grew in complexity and you wanted to filter out some records, but not others. I’m sure that one of your first reactions would, naturally, be to change the SQL query – assuming that we’re talking to a datasource that is an RDBMS. But:

  • What if it wasn’t?
  • What if your only access was an API?
  • What if you were not allowed to change the underlying code?
  • What if you didn’t know all the ramifications of what a change may be?

It’s not quite so easy is it? Let’s say that one or more of these constraints is in effect. Is there an answer? Yes, there most certainly is – so long as you’re using PHP >= 5.1.0 that is.

Enter the FilterIterator

That’s right, in PHP 5.1.0 as part of the SPL (Standard PHP Library), Filter Iterators were introduced. If you’ve not heard of them or used them, the PHP manual describes them as:

This abstract iterator filters out unwanted values. This class should be extended to implement custom iterator filters. The FilterIterator::accept() must be implemented in the subclass.

Iterators and the FilterIterator are a wonderful addition to the PHP language as they allow us, as developers, to apply the existing looping constructs to our custom data structures.

Recently in a Malt Blue project, we were tasked with building a project that provides data, that is largely similar in nature, which needed to be viewed across a range of different parts of the application. Given that the data was so similar in nature, it made no sense to write a series of different, utility, functions for it – which would then need to be maintained over time.

So we chose to instead implement a series of FilterIterators so that we could make one data request call and display the same information in a multitude of, slightly, different ways.

For the purposes of this example, let’s assume that the data structure is similar to the above. Have a look at the code below and we’ll work through it, showing how to implement a FilterIterator:

<?php
 
class MyIterator_Filter_InActive extends FilterIterator
{
    public function accept()
    {
        $value = $this->current();
        if (array_key_exists('active', $value) && 
            !empty($value['active'])) {
            return $value['active'];    
        }
        return false;       
    }   
}

In the code above, we define the class MyIterator_Filter_InActive which extends FilterIterator. The FilterIterator interface requires us to only implement one method: accept. If accept returns true, then the current record is displayed. If it returns false, then the record is skipped.

So, we retrieve the current record, with $value = $this->current();. We then check whether it has an active value and if that value is set. If it is, then we return it. If one of these conditions is not met, then we automatically return false.

So let’s instantiate the FilterIterator with our existing dataset and see what the result is.

<?php
$dataIterator = new MyIterator_Filter_InActive(
    new ArrayIterator($dataList)
);

As our data is already in an array, we start by using it to instantiate an ArrayIterator, which makes it painless to iterate over the whole dataset – not what we want. This instantiated ArrayIterator is then passed to our custom FilterIterator.

With this instantiated FilterIterator, we can then loop over the data as before, but now only with records that have active set as true – as the below code does:

<?php
foreach ($dataIterator as $userDetail) {
    printf(
        "Name: %s | Email: %s | Date Of Birth: %s | Location: %s",
        $userDetail["name"], 
        $userDetail["email"], 
        $userDetail["dob"], 
        $userDetail["location"]
    );
}

The updated output will be as below:

Name: John Citizen | Email: johnc@citizen.org | DOB: 1350910989 | Location: Brisbane, Qld, Australia
Name: Wendy Hardworker | Email: wendyh@citizen.org | DOB: 1350910989 | Location: Melbourne, Vic, Australia

Pretty simple, hey? Now yes, this was a simple example. But you can see how, through creating a custom FilterIterator, you can quickly and simply use the existing PHP looping constructs to iterate through your custom data sets returning only the information that you specifically need.

Questions

Do you use FilterIterators already? What’s your experience with them? Do they help you build more maintainable code? Share your experience in the comments. If you liked this post, then check out the follow up to it: filter by date of birth easily with a filteriterator.


Share This


About Matthew

Matthew Setter Matthew Setter is a software developer specializing in PHP, Zend Framework, and JavaScript. He's also the host of FreeTheGeek.fm, the podcast about the business of freelancing as a software developer and technical writer, and editor of Master Zend Framework, dedicated to helping you become a Zend Framework master?.

Find Matthew on LinkedIn Find Matthew on Twitter Find Matthew on Facebook Find Matthew on Google+

Like That?

Don’t miss the next post. Drop your email in the box below, and get it straight to your inbox, PLUS exclusive content only available by email. No spam, and you can unsubscribe at any time.