20
20
use Countable ;
21
21
use Generator ;
22
22
use Iterator ;
23
+ use IteratorIterator ;
23
24
use Traversable ;
24
25
use function count ;
25
26
use function current ;
@@ -41,7 +42,7 @@ class CachingIterator implements Countable, Iterator
41
42
/** @var array */
42
43
private $ items = [];
43
44
44
- /** @var Generator */
45
+ /** @var IteratorIterator */
45
46
private $ iterator ;
46
47
47
48
/** @var boolean */
@@ -50,6 +51,9 @@ class CachingIterator implements Countable, Iterator
50
51
/** @var boolean */
51
52
private $ iteratorExhausted = false ;
52
53
54
+ /** @var boolean */
55
+ private $ iteratorPrepared = false ;
56
+
53
57
/**
54
58
* Initialize the iterator and stores the first item in the cache. This
55
59
* effectively rewinds the Traversable and the wrapping Generator, which
@@ -61,8 +65,7 @@ class CachingIterator implements Countable, Iterator
61
65
*/
62
66
public function __construct (Traversable $ traversable )
63
67
{
64
- $ this ->iterator = $ this ->wrapTraversable ($ traversable );
65
- $ this ->storeCurrentItem ();
68
+ $ this ->iterator = new IteratorIterator ($ traversable );
66
69
}
67
70
68
71
/**
@@ -82,6 +85,8 @@ public function count()
82
85
*/
83
86
public function current ()
84
87
{
88
+ $ this ->prepareIterator ();
89
+
85
90
return current ($ this ->items );
86
91
}
87
92
@@ -91,6 +96,8 @@ public function current()
91
96
*/
92
97
public function key ()
93
98
{
99
+ $ this ->prepareIterator ();
100
+
94
101
return key ($ this ->items );
95
102
}
96
103
@@ -100,9 +107,15 @@ public function key()
100
107
*/
101
108
public function next ()
102
109
{
110
+ $ this ->prepareIterator ();
111
+
103
112
if (! $ this ->iteratorExhausted ) {
113
+ $ this ->iteratorAdvanced = true ;
104
114
$ this ->iterator ->next ();
115
+
105
116
$ this ->storeCurrentItem ();
117
+
118
+ $ this ->iteratorExhausted = !$ this ->iterator ->valid ();
106
119
}
107
120
108
121
next ($ this ->items );
@@ -114,6 +127,8 @@ public function next()
114
127
*/
115
128
public function rewind ()
116
129
{
130
+ $ this ->prepareIterator ();
131
+
117
132
/* If the iterator has advanced, exhaust it now so that future iteration
118
133
* can rely on the cache.
119
134
*/
@@ -138,11 +153,24 @@ public function valid()
138
153
*/
139
154
private function exhaustIterator ()
140
155
{
156
+ $ this ->prepareIterator ();
157
+
141
158
while (! $ this ->iteratorExhausted ) {
142
159
$ this ->next ();
143
160
}
144
161
}
145
162
163
+ private function prepareIterator ()
164
+ {
165
+ if ($ this ->iteratorPrepared ) {
166
+ return ;
167
+ }
168
+
169
+ $ this ->iterator ->rewind ();
170
+ $ this ->iteratorPrepared = true ;
171
+ $ this ->storeCurrentItem ();
172
+ }
173
+
146
174
/**
147
175
* Stores the current item in the cache.
148
176
*/
@@ -156,20 +184,4 @@ private function storeCurrentItem()
156
184
157
185
$ this ->items [$ key ] = $ this ->iterator ->current ();
158
186
}
159
-
160
- /**
161
- * Wraps the Traversable with a Generator.
162
- *
163
- * @param Traversable $traversable
164
- * @return Generator
165
- */
166
- private function wrapTraversable (Traversable $ traversable )
167
- {
168
- foreach ($ traversable as $ key => $ value ) {
169
- yield $ key => $ value ;
170
- $ this ->iteratorAdvanced = true ;
171
- }
172
-
173
- $ this ->iteratorExhausted = true ;
174
- }
175
187
}
0 commit comments