Skip to content

Commit 7b614de

Browse files
committed
Clean up some uncommited stuff
From local repo: This looks pretty clean now -- the SplitIterator is well documented. Also one additional test case right from the new documentation.
1 parent 35f2ba6 commit 7b614de

File tree

2 files changed

+188
-17
lines changed

2 files changed

+188
-17
lines changed

SplitIterator.php

+146-17
Original file line numberDiff line numberDiff line change
@@ -2,32 +2,147 @@
22
require_once("SplitIterator/SplitInnerIterator.php");
33

44
/**
5+
* An {@see IteratorIterator} that splits another iterator ({@see
6+
* Traversable}) into multiple iterators.
7+
*
8+
* This is a template (an abstract base class) for generic splitting
9+
* iterators: You just need to subclass it and implement the method
10+
* {@see needsSplit()} that decides on where the inner iterator should
11+
* be split up.
12+
*
13+
* See {@see SplitCallbackIterator} for an alternative implementation,
14+
* in case you don't want to subclass, but rather just hand in a
15+
* callback to do the split decision in a ready-made class.
16+
*
17+
* A minimal example for a splitting iterator that splits any other
18+
* iterator with numeric keys (at each key position that divides by
19+
* three without remainder):
20+
*
21+
* <code>
22+
* // Inherit from SplitIterator
23+
* class SplitAtKeyDivisibleByThree extends SplitIterator {
24+
*
25+
* // Just add the split decision: It gets called with each
26+
* // item's key and value -- a split takes place if this
27+
* // method returns TRUE
28+
* public function needsSplit($key, $item) {
29+
* // Divide the key by three (modulus), and if there's no
30+
* // remainder, split!
31+
* return $key % 3 == 0;
32+
* }
33+
* }
34+
* </code>
35+
*
36+
* Let's apply this trivial example to an Iterator that returns the
37+
* letters 'a' to 'z' with the keys 1 to 26:
38+
*
39+
* <code>
40+
* // Quick way to construct an iterator over 1..26 => 'a'..'z'
41+
* $testArray = array_combine(range(1, 26), range('a', 'z'));
42+
* $testIterator = new ArrayIterator($testArray);
43+
*
44+
* // Now apply our splitter to it
45+
* $splitter = new SplitAtKeyDivisibleByThree($testIterator);
46+
*
47+
* // And we have the letters arranged into groups of three,
48+
* // so the following prints:
49+
* // abc
50+
* // def
51+
* // ghi
52+
* // ...
53+
* foreach($splitter as $group) {
54+
* foreach($group as $letter)
55+
* print $letter;
56+
* print "\n";
57+
* }
58+
* </code>
59+
*
60+
* The inner iterator is in fact not really "physically" split: This
61+
* class just generates a sort of dynamic "view" on top of it. The
62+
* advantage of this being that no real additional memory overhead is
63+
* created, even if the items in the inner iterator are large. A
64+
* possible disadvantage is, that the {@see SplitInnerIterator}s
65+
* returned for the individual splits can't be rewound and only one of
66+
* them can be iterated over at any given time (once you retrieve the
67+
* next one of the split-off iterators, the last one is invalidated).
68+
* This is due to the fact that jumping back and forward between
69+
* individual splits, and rewinding them alone, would actually mean to
70+
* seek randomly through the inner iterator.
71+
*
72+
* For most applications (that resemble two nested loops, as in the
73+
* example above) these restrictions however do not matter. If they
74+
* do, you might want to just override {@see current()} and copy the
75+
* contents of the {@see SplitInnerIterator} into another type of
76+
* iterator that can live independently.
577
*
678
* @author Beat Vontobel
779
* @since 2015-05-20
880
* @package MeteoNews\phplib
981
* @subpackage Iterators
82+
*
83+
* @see SplitCallbackIterator SplitInnerIterator
1084
*/
1185
abstract class SplitIterator extends IteratorIterator {
12-
abstract public function needsSplit($key, $value);
86+
/**
87+
* The current item (which is the split-off iterator) returned from
88+
* this class.
89+
*
90+
* @val NULL|SplitInnerIterator
91+
*/
92+
private $currentSplitIterator;
1393

14-
private $splitIterator;
94+
/**
95+
* The key for the current item returned by this class. We generate
96+
* them as integer indices, starting at 0.
97+
*
98+
* @val int
99+
*/
100+
private $key = 0;
15101

16-
private $key;
17-
18-
public function __construct(Traversable $innerIterator) {
19-
parent::__construct($innerIterator);
20-
$this->splitOff();
21-
}
102+
/**
103+
* Decide if it's necessary to split-off a new iterator.
104+
*
105+
* Every child needs to implement this method: It is called for each
106+
* item returned from the inner iterator, with that item's key and
107+
* value. The splitting iterator needs to return TRUE if this item
108+
* should split-off a new iterator.
109+
*
110+
* @param scalar The key of the current item as returned by the
111+
* inner iterator.
112+
*
113+
* @param mixed The current value as returned by the inner iterator.
114+
*
115+
* @return bool TRUE if a new split-off iterator should be started
116+
* for the current item returned from the inner iterator.
117+
*/
118+
abstract public function needsSplit($key, $current);
22119

120+
/**
121+
* The {@see SplitIterator} returns generated integer keys for the
122+
* split-off iterators it creates.
123+
*
124+
* The index starts at 0 for the first iterator returned.
125+
*
126+
* @return int
127+
*/
23128
public function key() {
24129
return $this->key;
25130
}
26131

132+
/**
133+
* Returns the currently active split-off iterator.
134+
*
135+
* @return SplitInnerIterator
136+
*/
27137
public function current() {
28-
return $this->splitIterator;
138+
return $this->currentSplitIterator;
29139
}
30140

141+
/**
142+
* @todo Handle the situation where the outside calls this
143+
* "prematurely" (before the split-off iterator has been
144+
* iterated through).
145+
*/
31146
public function next() {
32147
parent::next();
33148
$this->key++;
@@ -40,18 +155,32 @@ public function rewind() {
40155
$this->splitOff();
41156
}
42157

158+
public function valid() {
159+
return isset($this->currentSplitIterator);
160+
}
161+
162+
/**
163+
* Splits off a new {@see SplitInnerIterator} and makes it our
164+
* current item.
165+
*
166+
* Invalidates a possibly earlier created {@see SplitInnerIterator},
167+
* and checks if we really have more items available before
168+
* splitting off.
169+
*
170+
* This internal method exists as the exact same code flow is needed
171+
* in {@see rewind()} as well as {@see next()}.
172+
*
173+
* @return void
174+
*/
43175
private function splitOff() {
44-
if(isset($this->splitIterator)) {
45-
$this->splitIterator->invalidate();
46-
$this->splitIterator = NULL;
176+
if(isset($this->currentSplitIterator)) {
177+
$this->currentSplitIterator->invalidate();
178+
$this->currentSplitIterator = NULL;
47179
}
48180

49181
if(parent::valid())
50-
$this->splitIterator = new SplitInnerIterator($this->getInnerIterator(), $this);
51-
}
52-
53-
public function valid() {
54-
return isset($this->splitIterator);
182+
$this->currentSplitIterator
183+
= new SplitInnerIterator($this->getInnerIterator(), $this);
55184
}
56185
}
57186
?>

tests/SplitIterator_002.phpt

+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
--TEST--
2+
SplitIterator: Documentation example
3+
--FILE--
4+
<?php
5+
require_once("SplitIterator.php");
6+
7+
// Inherit from SplitIterator
8+
class SplitAtKeyDivisibleByThree extends SplitIterator {
9+
10+
// Just add the split decision: It gets called with each
11+
// item's key and value -- a split takes place if this
12+
// method returns TRUE
13+
public function needsSplit($key, $item) {
14+
// Divide the key by three (modulus), and if there's no
15+
// remainder, split!
16+
return $key % 3 == 0;
17+
}
18+
}
19+
20+
$origIterator = new ArrayIterator(array_combine(range(1, 26), range('a', 'z')));
21+
22+
$splitter = new SplitAtKeyDivisibleByThree($origIterator);
23+
24+
foreach($splitter as $group) {
25+
print "'";
26+
foreach($group as $letter) {
27+
print $letter;
28+
}
29+
print "'\n";
30+
}
31+
32+
?>
33+
--EXPECT--
34+
'abc'
35+
'def'
36+
'ghi'
37+
'jkl'
38+
'mno'
39+
'pqr'
40+
'stu'
41+
'vwx'
42+
'yz'

0 commit comments

Comments
 (0)