The IteratorAggregate
is an interface to create an external Iterator:
class myData implements IteratorAggregate
{
public $property1 = "Public property one";
public $property2 = "Public property two";
public $property3 = "Public property three";
public function __construct()
{
$this->property4 = "last property";
}
public function getIterator()
{
return new ArrayIterator($this);
}
}
$obj = new myData;
And you'll be able to traverse the object using foreach
:
foreach($obj as $key => $value) {
var_dump($key, $value);
echo "\n";
}
While Iterator
is an interface for external iterators or objects that can be iterated themselves internally:
class myIterator implements Iterator
{
private $position = 0;
private $array = array('one', 'two', 'three');
function rewind()
{
$this->position = 0;
}
function current()
{
return $this->array[$this->position];
}
function key()
{
return $this->position;
}
function next()
{
++$this->position;
}
function valid()
{
return isset($this->array[$this->position]);
}
}
And again, you can traverse it basically the same way:
$it = new myIterator;
foreach($it as $key => $value) {
var_dump($key, $value);
echo "\n";
}
So can anyone explain why we need two interfaces and what's the difference between them?
I assume IteratorAggregate
is for cases where you want to save time, and Iterator
is for cases where you need fine control over iteration through your object. For example it lets you add custom exceptions on next()
, key()
or prev()
failures, caching(if you iterate through something that takes data over the network), preprocess values before returning them.
IteratorAggregate
is an easy way to implement an Iterator. The disadvantage is you cannot add next()
, key()
, etc methods, as they won't get called during a normal foreach
traversal.
If you need to implement custom methods, you need to implement an OuterIterator
or (easier) to extend an IteratorIterator
.
The advantage with IteratorAggregate
is the speed, it is much more faster than its alternatives. The problem with it it's that... despite the name it's not an iterator but a traversable (so again, no next, key, current, valid, rewind methods).
Here's a simple benchmark I run on my poor laptop with 1 million of iterations for each one of IteratorAggregate
, IteratorIterator
and OuterIterator
both using iterator_to_array
and foreach
:
(also note the echo
inside Aggregate's next
method: no 'x' gets printed, proof it is never invoked)
$ repeat 3 php benchIterator.php
------------------------------------
Outer_ToArray: 569.61703300476 ms
Aggregate_ToArray: 552.38103866577 ms
IteratorIt_ToArray: 546.95200920105 ms
Outer_Foreach: 1679.8989772797 ms
IteratorIt_Foreach: 1019.6850299835 ms
Aggregate_Foreach: 367.35391616821 ms
------------------------------------
Outer_ToArray: 570.75309753418 ms
Aggregate_ToArray: 544.40784454346 ms
IteratorIt_ToArray: 555.06300926208 ms
Outer_Foreach: 1682.2130680084 ms
IteratorIt_Foreach: 988.21592330933 ms
Aggregate_Foreach: 356.41598701477 ms
------------------------------------
Outer_ToArray: 566.06101989746 ms
Aggregate_ToArray: 543.1981086731 ms
IteratorIt_ToArray: 546.28610610962 ms
Outer_Foreach: 1663.2289886475 ms
IteratorIt_Foreach: 995.28503417969 ms
Aggregate_Foreach: 356.16087913513 ms
Here's the code I used for the benchmark:
<?php
class Aggregate implements \IteratorAggregate
{
protected $var;
public function __construct($var = null)
{
if (is_array($var)) {
$this->var = new ArrayIterator($var);
}
if ($var instanceof \Traversable) {
$this->var = $var;
}
}
public function next()
{
echo 'x';
}
public function toArray()
{
return iterator_to_array($this->var, true);
}
public function getIterator()
{
return $this->var;
}
}
class Outer implements \OuterIterator
{
protected $var;
public function __construct($var = null)
{
if (is_array($var)) {
$this->var = new ArrayIterator($var);
}
if ($var instanceof \Traversable) {
$this->var = $var;
}
}
public function toArray()
{
return iterator_to_array($this->var, true);
}
public function getInnerIterator()
{
return $this->var;
}
public function current()
{
return $this->var->current();
}
public function next()
{
$this->var->next();
}
public function key()
{
return $this->var->key();
}
public function valid()
{
return $this->var->valid();
}
public function rewind()
{
$this->var->rewind();
}
}
class IteratorIt extends IteratorIterator
{
public function __construct($var = null)
{
if (is_array($var)) {
$var = new ArrayIterator($var);
}
parent::__construct($var);
}
public function toArray()
{
return iterator_to_array($this->getInnerIterator(), true);
}
public function getIterator()
{
return $this->getInnerIterator();
}
}
function bench($name, $test)
{
echo "$name: ";
$start = microtime(true);
$test();
$time = microtime(true);
$time -= $start;
echo ($time * 1000) . ' ms' . PHP_EOL;
}
$max = 1e6;
$array = range (1, 1e6);
$testSuites = [
'Outer_ToArray' => function () use ($max, $array) {
$iterator = new Outer($array);
$r = $iterator->toArray();
},
'Aggregate_ToArray' => function () use ($max, $array) {
$iterator = new Aggregate($array);
$r = $iterator->toArray();
},
'IteratorIt_ToArray' => function () use ($max, $array) {
$iterator = new IteratorIt($array);
$r = $iterator->toArray();
},
'Outer_Foreach' => function () use ($max, $array) {
$iterator = new Outer($array);
foreach ($iterator as $k => $v)
{
}
},
'IteratorIt_Foreach' => function () use ($max, $array) {
$iterator = new IteratorIt($array);
foreach ($iterator as $k => $v)
{
}
},
'Aggregate_Foreach' => function () use ($max, $array) {
$iterator = new Aggregate($array);
foreach ($iterator as $k => $v)
{
}
},
];
echo '------------------------------------'.PHP_EOL;
foreach ($testSuites as $name => $test) {
bench($name, $test);
}
What's the difference between them?
The difference in names("Aggregate").
What does Aggregate mean?
Aggregate state guarantees the consistency of changes being made within the aggregate by forbidding external objects from holding references to its members. (wikipedia)
The TC code:
class myData implements IteratorAggregate {//your implementation}
class myIterator implements Iterator {//your implementation}
Here's the code for clarity of Aggregate:
$renderFunction = function($iterator) {
foreach($iterator as $key => $value) {
echo "$key: $value\n";
foreach($iterator as $key => $value) {
echo " $key: $value\n";
}
}
};
$renderFunction(new myData); // Aggregate on
$renderFunction(new myIterator); // Aggregate off
What do you expect in cases?
https://3v4l.org/JHDNQ
property1: Public property one
property1: Public property one
property2: Public property two
property3: Public property three
property4: last property
property2: Public property two
property1: Public property one
property2: Public property two
property3: Public property three
property4: last property
property3: Public property three
property1: Public property one
property2: Public property two
property3: Public property three
property4: last property
property4: last property
property1: Public property one
property2: Public property two
property3: Public property three
property4: last property
vs
0: one
0: one
1: two
2: three
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With