Reputation: 5311
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
Reputation: 77400
Extensibility & abstraction.
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 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.
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):
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).
Another use for iterators is in-place data processing. Processing iterators wrap other iterators, which allows for iterator composition. In PHP, these are the OuterIterator
s 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 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 OuterIterator
s); 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 implementedfold
: 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).CachingIterator
. Not so much a sequence operation as an (FP language) feature for function results.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
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