1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
declare(strict_types=1); |
4
|
|
|
|
5
|
|
|
namespace Doctrine\ODM\MongoDB\Iterator; |
6
|
|
|
|
7
|
|
|
use Generator; |
8
|
|
|
use Traversable; |
9
|
|
|
|
10
|
|
|
/** |
11
|
|
|
* Iterator for wrapping a Traversable and caching its results. |
12
|
|
|
* |
13
|
|
|
* By caching results, this iterators allows a Traversable to be counted and |
14
|
|
|
* rewound multiple times, even if the wrapped object does not natively support |
15
|
|
|
* those operations (e.g. MongoDB\Driver\Cursor). |
16
|
|
|
* |
17
|
|
|
* @internal |
18
|
|
|
*/ |
19
|
|
|
final class BaseIterator implements Iterator |
20
|
|
|
{ |
21
|
|
|
private $key; |
22
|
|
|
|
23
|
|
|
private $current; |
24
|
|
|
|
25
|
|
|
/** @var Generator */ |
26
|
|
|
private $iterator; |
27
|
|
|
|
28
|
|
|
/** @var bool */ |
29
|
|
|
private $iteratorAdvanced = false; |
30
|
|
|
|
31
|
|
|
/** |
32
|
|
|
* Initialize the iterator and stores the first item in the cache. This |
33
|
|
|
* effectively rewinds the Traversable and the wrapping Generator, which |
34
|
|
|
* will execute up to its first yield statement. Additionally, this mimics |
35
|
|
|
* behavior of the SPL iterators and allows users to omit an explicit call |
36
|
|
|
* to rewind() before using the other methods. |
37
|
|
|
*/ |
38
|
7 |
|
public function __construct(Traversable $iterator) |
39
|
|
|
{ |
40
|
7 |
|
$this->iterator = $this->wrapTraversable($iterator); |
41
|
7 |
|
$this->storeCurrentItem(); |
42
|
7 |
|
} |
43
|
|
|
|
44
|
2 |
|
public function toArray() : array |
45
|
|
|
{ |
46
|
2 |
|
if ($this->iteratorAdvanced) { |
47
|
1 |
|
throw new \LogicException(sprintf( |
48
|
1 |
|
'Cannot call %s for iterator that already yielded results', |
49
|
1 |
|
__METHOD__ |
50
|
|
|
)); |
51
|
|
|
} |
52
|
|
|
|
53
|
|
|
$wrapper = function () { |
54
|
1 |
|
yield $this->key => $this->current; |
55
|
1 |
|
yield from $this->iterator; |
56
|
1 |
|
}; |
57
|
|
|
|
58
|
1 |
|
return iterator_to_array($wrapper()); |
59
|
|
|
} |
60
|
|
|
|
61
|
|
|
/** |
62
|
|
|
* @see http://php.net/iterator.current |
63
|
|
|
* |
64
|
|
|
* @return mixed |
65
|
|
|
*/ |
66
|
5 |
|
public function current() |
67
|
|
|
{ |
68
|
5 |
|
return $this->current; |
69
|
|
|
} |
70
|
|
|
|
71
|
|
|
/** |
72
|
|
|
* @see http://php.net/iterator.mixed |
73
|
|
|
* |
74
|
|
|
* @return mixed |
75
|
|
|
*/ |
76
|
5 |
|
public function key() |
77
|
|
|
{ |
78
|
5 |
|
return $this->key; |
79
|
|
|
} |
80
|
|
|
|
81
|
|
|
/** |
82
|
|
|
* @see http://php.net/iterator.next |
83
|
|
|
*/ |
84
|
4 |
|
public function next() : void |
85
|
|
|
{ |
86
|
4 |
|
if ($this->iterator) { |
87
|
4 |
|
$this->iterator->next(); |
88
|
|
|
} |
89
|
4 |
|
} |
90
|
|
|
|
91
|
|
|
/** |
92
|
|
|
* @see http://php.net/iterator.rewind |
93
|
|
|
*/ |
94
|
5 |
|
public function rewind() : void |
95
|
|
|
{ |
96
|
|
|
/* If the iterator has advanced, exhaust it now so that future iteration |
97
|
|
|
* can rely on the cache. |
98
|
|
|
*/ |
99
|
5 |
|
if ($this->iteratorAdvanced) { |
100
|
1 |
|
throw new \LogicException(sprintf( |
101
|
1 |
|
'Cannot call %s for iterator that already yielded results', |
102
|
1 |
|
__METHOD__ |
103
|
|
|
)); |
104
|
|
|
} |
105
|
5 |
|
} |
106
|
|
|
|
107
|
|
|
/** |
108
|
|
|
* @see http://php.net/iterator.valid |
109
|
|
|
*/ |
110
|
6 |
|
public function valid() : bool |
111
|
|
|
{ |
112
|
6 |
|
return $this->key !== null; |
113
|
|
|
} |
114
|
|
|
|
115
|
|
|
/** |
116
|
|
|
* Stores the current item in the cache. |
117
|
|
|
*/ |
118
|
7 |
|
private function storeCurrentItem() : void |
119
|
|
|
{ |
120
|
7 |
|
$this->key = $this->iterator->key(); |
121
|
|
|
|
122
|
7 |
|
if ($this->key === null) { |
123
|
1 |
|
return; |
124
|
|
|
} |
125
|
|
|
|
126
|
6 |
|
$this->current = $this->iterator->current(); |
127
|
6 |
|
} |
128
|
|
|
|
129
|
7 |
|
private function wrapTraversable(Traversable $traversable) : Generator |
130
|
|
|
{ |
131
|
7 |
View Code Duplication |
foreach ($traversable as $key => $value) { |
|
|
|
|
132
|
6 |
|
$this->key = $key; |
133
|
6 |
|
$this->current = $value; |
134
|
6 |
|
yield $key => $value; |
135
|
5 |
|
$this->iteratorAdvanced = true; |
136
|
|
|
} |
137
|
|
|
|
138
|
3 |
|
$this->iterator = null; |
139
|
3 |
|
$this->key = null; |
140
|
3 |
|
$this->current = null; |
141
|
3 |
|
} |
142
|
|
|
} |
143
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.