2
2
require_once ("SplitIterator/SplitInnerIterator.php " );
3
3
4
4
/**
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.
5
77
*
6
78
* @author Beat Vontobel
7
79
* @since 2015-05-20
8
80
* @package MeteoNews\phplib
9
81
* @subpackage Iterators
82
+ *
83
+ * @see SplitCallbackIterator SplitInnerIterator
10
84
*/
11
85
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 ;
13
93
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 ;
15
101
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 );
22
119
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
+ */
23
128
public function key () {
24
129
return $ this ->key ;
25
130
}
26
131
132
+ /**
133
+ * Returns the currently active split-off iterator.
134
+ *
135
+ * @return SplitInnerIterator
136
+ */
27
137
public function current () {
28
- return $ this ->splitIterator ;
138
+ return $ this ->currentSplitIterator ;
29
139
}
30
140
141
+ /**
142
+ * @todo Handle the situation where the outside calls this
143
+ * "prematurely" (before the split-off iterator has been
144
+ * iterated through).
145
+ */
31
146
public function next () {
32
147
parent ::next ();
33
148
$ this ->key ++;
@@ -40,18 +155,32 @@ public function rewind() {
40
155
$ this ->splitOff ();
41
156
}
42
157
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
+ */
43
175
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 ;
47
179
}
48
180
49
181
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 );
55
184
}
56
185
}
57
186
?>
0 commit comments