1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
declare(strict_types=1); |
4
|
|
|
|
5
|
|
|
namespace Doctrine\ODM\MongoDB\Iterator; |
6
|
|
|
|
7
|
|
|
use Generator; |
8
|
|
|
use LogicException; |
9
|
|
|
use RuntimeException; |
10
|
|
|
use Traversable; |
11
|
|
|
use function iterator_to_array; |
12
|
|
|
use function sprintf; |
13
|
|
|
|
14
|
|
|
/** |
15
|
|
|
* Iterator for wrapping a Traversable/Cursor. |
16
|
|
|
* |
17
|
|
|
* @internal |
18
|
|
|
*/ |
19
|
|
|
final class UnrewindableIterator implements Iterator |
20
|
|
|
{ |
21
|
|
|
/** @var Generator|null */ |
22
|
|
|
private $iterator; |
23
|
|
|
|
24
|
|
|
/** @var bool */ |
25
|
|
|
private $iteratorAdvanced = false; |
26
|
|
|
|
27
|
|
|
/** |
28
|
|
|
* Initialize the iterator. This effectively rewinds the Traversable and |
29
|
|
|
* the wrapping Generator, which will execute up to its first yield statement. |
30
|
|
|
* Additionally, this mimics behavior of the SPL iterators and allows users |
31
|
|
|
* to omit an explicit call to rewind() before using the other methods. |
32
|
|
|
*/ |
33
|
11 |
|
public function __construct(Traversable $iterator) |
34
|
|
|
{ |
35
|
11 |
|
$this->iterator = $this->wrapTraversable($iterator); |
36
|
11 |
|
$this->iterator->key(); |
37
|
11 |
|
} |
38
|
|
|
|
39
|
3 |
|
public function toArray() : array |
40
|
|
|
{ |
41
|
3 |
|
$this->preventRewinding(__METHOD__); |
42
|
|
|
|
43
|
|
|
$toArray = function () { |
44
|
2 |
|
if (! $this->valid()) { |
45
|
1 |
|
return; |
46
|
|
|
} |
47
|
1 |
|
yield $this->key() => $this->current(); |
48
|
1 |
|
yield from $this->getIterator(); |
49
|
2 |
|
}; |
50
|
|
|
|
51
|
2 |
|
return iterator_to_array($toArray()); |
52
|
|
|
} |
53
|
|
|
|
54
|
|
|
/** |
55
|
|
|
* @see http://php.net/iterator.current |
56
|
|
|
* |
57
|
|
|
* @return mixed |
58
|
|
|
*/ |
59
|
7 |
|
public function current() |
60
|
|
|
{ |
61
|
7 |
|
return $this->getIterator()->current(); |
62
|
|
|
} |
63
|
|
|
|
64
|
|
|
/** |
65
|
|
|
* @see http://php.net/iterator.mixed |
66
|
|
|
* |
67
|
|
|
* @return mixed |
68
|
|
|
*/ |
69
|
9 |
|
public function key() |
70
|
|
|
{ |
71
|
9 |
|
if ($this->iterator) { |
72
|
7 |
|
return $this->iterator->key(); |
73
|
|
|
} |
74
|
|
|
|
75
|
4 |
|
return null; |
76
|
|
|
} |
77
|
|
|
|
78
|
|
|
/** |
79
|
|
|
* @see http://php.net/iterator.next |
80
|
|
|
*/ |
81
|
5 |
|
public function next() : void |
82
|
|
|
{ |
83
|
5 |
|
if (! $this->iterator) { |
84
|
|
|
return; |
85
|
|
|
} |
86
|
|
|
|
87
|
5 |
|
$this->iterator->next(); |
88
|
5 |
|
} |
89
|
|
|
|
90
|
|
|
/** |
91
|
|
|
* @see http://php.net/iterator.rewind |
92
|
|
|
*/ |
93
|
6 |
|
public function rewind() : void |
94
|
|
|
{ |
95
|
6 |
|
$this->preventRewinding(__METHOD__); |
96
|
6 |
|
} |
97
|
|
|
|
98
|
|
|
/** |
99
|
|
|
* @see http://php.net/iterator.valid |
100
|
|
|
*/ |
101
|
9 |
|
public function valid() : bool |
102
|
|
|
{ |
103
|
9 |
|
return $this->key() !== null; |
104
|
|
|
} |
105
|
|
|
|
106
|
8 |
|
private function preventRewinding(string $method) : void |
107
|
|
|
{ |
108
|
8 |
|
if ($this->iteratorAdvanced) { |
109
|
3 |
|
throw new LogicException(sprintf( |
110
|
3 |
|
'Cannot call %s for iterator that already yielded results', |
111
|
3 |
|
$method |
112
|
|
|
)); |
113
|
|
|
} |
114
|
8 |
|
} |
115
|
|
|
|
116
|
7 |
|
private function getIterator() : Generator |
117
|
|
|
{ |
118
|
7 |
|
if ($this->iterator === null) { |
119
|
|
|
throw new RuntimeException('Iterator has already been destroyed'); |
120
|
|
|
} |
121
|
|
|
|
122
|
7 |
|
return $this->iterator; |
123
|
|
|
} |
124
|
|
|
|
125
|
11 |
View Code Duplication |
private function wrapTraversable(Traversable $traversable) : Generator |
|
|
|
|
126
|
|
|
{ |
127
|
11 |
|
foreach ($traversable as $key => $value) { |
128
|
7 |
|
yield $key => $value; |
129
|
6 |
|
$this->iteratorAdvanced = true; |
130
|
|
|
} |
131
|
|
|
|
132
|
7 |
|
$this->iterator = null; |
133
|
7 |
|
} |
134
|
|
|
} |
135
|
|
|
|
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.