Spencer Mark
Spencer Mark

Reputation: 5311

PHP Iterator classes

I'm trying to figure out what's the actual benefit of using Iterator classes in Object Oriented PHP over the standard array.

I'm planning to upgrade my framework by converting all arrays to object, but I just don't understand the actual need apart from having the system being fully OOP.

I know that by the use of IteratorAggregate I can create:

class MyModel implements IteratorAggregate {

    public $records = array();

    public function __construct(array $records) {

        $this->records = $records;

    }

    public function getIterator() {

        return new ArrayIterator($this->records);

    }

}

and then simply loop through it like using the array:

$mdlMy = new MyModel(array(
    array('first_name' => 'Mark', 'last_name' => 'Smith'),
    array('first_name' => 'John', 'last_name' => 'Simpson')
));



foreach($mdlMy as $row) {

    echo $row['first_name'];
    echo $row['last_name'];

}

Could someone in simple terms explain the actual purpose of these - perhaps with some use case.

Upvotes: 3

Views: 1011

Answers (2)

outis
outis

Reputation: 77400

Shortest Answer

Extensibility & abstraction.

Abstract Answer

As soon as you have the ArrayAccess interface, you've got things that aren't arrays but have an array interface. How will you traverse these? You could do it directly, which is where the Iterator interface comes from. Iterator might not make sense for some classes, either due to the single-responsibility principle, or for performance's sake, which is where you get IteratorAggregate.

SPL-based Answer

SPL introduced a number of data structures. Iterators allow these to be traversed in foreach loops. Without iterators, collections would need to be converted to arrays, a potentially costly operation.

Long Answer

Source-Iterators

The first use comes up with data sources (e.g. collections), which aren't all natively held in arrays. Examples (note: there is some overlap):

  • trees
  • the file system
  • the previously mentioned SPL data structures
  • network communications
  • database query results
  • external process results
  • ongoing computation (PHP 5.5 introduces generators for this case)

Any collection that isn't array-based typically either is an iterator or has a corresponding iterator. Without iterators, each of the above would need to be converted to or collected in an array, which might incur heavy time & space costs. If you only had arrays available for iteration, the process can't proceed until the conversion/collection finishes. Iterators allow for partial results to be processed as they become available, and for only portions of collections to be in memory at any point in time.

In particular case outlined in the question, the UserEntityManager::getAll() method could benefit from an Iterator by reducing memory usage. Depending on what is used for data storage, an Iterator will allow just some user records to be processed at a time, rather than loading all at once.

ArrayIterator, DirectoryIterator, and the SPL data structures are all examples of source-iterators (i.e. they iterate over a data source).

Processing-Iterators

Another use for iterators is in-place data processing. Processing iterators wrap other iterators, which allows for iterator composition. In PHP, these are the OuterIterators and sometimes have 'IteratorIterator' in their names.

You might ask "Why not just use functions?" The answer is that you could, but iterator composition (like function composition) is another (powerful) tool that allows for different types of solutions, sometimes achieving better performance or clarity. In particular, functions become a choke point in PHP, since it doesn't have in-language concurrency. Functions must finish before returning a result, which can be costly in terms of time & space, just as using arrays for iteration can be costly. Shallow.

The choke-point could be side-stepped by returning an iterator from a function, but that places a function call in between each iterator. Iterator composition allows deep iterator-based computations, cutting out the middle-man.

As for use-cases, consider a batch processing system that consumes data from multiple feeds, all of which have different formats. An adapting iterator can normalize the data for processing, allowing a single batch processor to service all the feeds.

As a reality check, in PHP you typically don't go full iterator-style any more than you'd write full-FP style, though PHP supports it. You usually don't compose more than a few iterators at a time (just as you often don't compose more than a few functions at a time in languages with function composition), and you don't create numerous iterators instead of functions.

RecursiveIteratorIterator is an example of a processing iterator; it linearizes a tree (simplifying tree traversal).

Iterator & Functional Styles

Iterator composition allows for a style closer to functional programming. At its most basic, an iterator is (roughly) a sequence. In FP, the most basic operation is fold (aka reduce), though others (especially append/concat, filter and map) are often implemented natively rather than in terms of fold for performance. PHP supports a few sequence operations on iterators (usually as OuterIterators); many are missing, but are easy to implement.

  • append: AppendIterator
  • cons: nothing, but easily (though not efficiently) implemented by creating an iterator that takes a single value, converting it to a single-element sequence, along with AppendIterator. EmptyIterator represents the empty sequence.
  • filter: CallbackFilterIterator
  • convolute (aka zip): MultipleIterator
  • slice: LimitIterator
  • map - nothing, but easily implemented
  • fold: nothing. Using a foreach loop and accumulating a value in a variable is probably clearer than implementing fold, but if you find a reason to do so, it's also straightforward (though probably not as an iterator).
  • flat-map: nothing. Can be written fairly easily (though not efficiently) in terms of append and map.
  • cycle: InfiniteIterator
  • unfold: generators (which are, in general, just a special case of iterators).
  • memoization: CachingIterator. Not so much a sequence operation as an (FP language) feature for function results.

The Future

Part of any language design is considering what the language could be. If concurrency were ever added to PHP, code that uses iterators (especially processing-iterators) could be made concurrent without being changed by making the iterators themselves concurrent.

Upvotes: 2

Nanne
Nanne

Reputation: 64399

Like this you have no advantage. Yet. But as soon as you need to implement some new functionality, you are having fun!

If you want to add a record to your 'array', but need to do some checking first, you'll have that in all instances: you don't have to find all the places you use this array, but you'll just add your validation to the constructor.

All other things that could go in a model will be your advantage.

But beware, 'OOP' is not "I'm using objects me!". It is also about what objects you have, what they do, etc. So don't just go packing all your arrays in objects and call it "going fully OOP". There's nothing OOP about that, nor is there a limit on using arrays in an OOP project.

Upvotes: 0

Related Questions