Completed
Pull Request — master (#2116)
by
unknown
16:29
created

UnrewindableIterator::toArray()   A

Complexity

Conditions 2
Paths 1

Size

Total Lines 14

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 14
ccs 8
cts 8
cp 1
rs 9.7998
c 0
b 0
f 0
cc 2
nc 1
nop 0
crap 2
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 and caching its results.
16
 *
17
 * By caching results, this iterators allows a Traversable to be counted and
18
 * rewound multiple times, even if the wrapped object does not natively support
19
 * those operations (e.g. MongoDB\Driver\Cursor).
20
 *
21
 * @internal
22
 */
23
final class UnrewindableIterator implements Iterator
24
{
25
    /** @var Generator|null */
26
    private $iterator;
27
28
    /** @var bool */
29
    private $iteratorAdvanced = false;
30
31
    /**
32
     * Initialize the iterator. This effectively rewinds the Traversable and
33
     * the wrapping Generator, which will execute up to its first yield statement.
34
     * Additionally, this mimics behavior of the SPL iterators and allows users
35
     * to omit an explicit call to rewind() before using the other methods.
36
     */
37 8
    public function __construct(Traversable $iterator)
38
    {
39 8
        $this->iterator = $this->wrapTraversable($iterator);
40 8
        $this->iterator->key();
41 8
    }
42
43 3
    public function toArray() : array
44
    {
45 3
        $this->preventRewinding(__METHOD__);
46
47
        $toArray = function () {
48 2
            if (! $this->valid()) {
49 1
                return;
50
            }
51 1
            yield $this->key() => $this->current();
52 1
            yield from $this->getIterator();
53 2
        };
54
55 2
        return iterator_to_array($toArray());
56
    }
57
58
    /**
59
     * @see http://php.net/iterator.current
60
     *
61
     * @return mixed
62
     */
63 6
    public function current()
64
    {
65 6
        return $this->getIterator()->current();
66
    }
67
68
    /**
69
     * @see http://php.net/iterator.mixed
70
     *
71
     * @return mixed
72
     */
73 8
    public function key()
74
    {
75 8
        if ($this->iterator) {
76 6
            return $this->iterator->key();
77
        }
78
79 3
        return null;
80
    }
81
82
    /**
83
     * @see http://php.net/iterator.next
84
     */
85 4
    public function next() : void
86
    {
87 4
        if (! $this->iterator) {
88
            return;
89
        }
90
91 4
        $this->iterator->next();
92 4
    }
93
94
    /**
95
     * @see http://php.net/iterator.rewind
96
     */
97 5
    public function rewind() : void
98
    {
99 5
        $this->preventRewinding(__METHOD__);
100 5
    }
101
102
    /**
103
     * @see http://php.net/iterator.valid
104
     */
105 8
    public function valid() : bool
106
    {
107 8
        return $this->key() !== null;
108
    }
109
110 7
    private function preventRewinding(string $method) : void
111
    {
112 7
        if ($this->iteratorAdvanced) {
113 2
            throw new LogicException(sprintf(
114 2
                'Cannot call %s for iterator that already yielded results',
115 2
                $method
116
            ));
117
        }
118 7
    }
119
120 6
    private function getIterator() : Generator
121
    {
122 6
        if ($this->iterator === null) {
123
            throw new RuntimeException('Iterator has already been destroyed');
124
        }
125
126 6
        return $this->iterator;
127
    }
128
129 8 View Code Duplication
    private function wrapTraversable(Traversable $traversable) : Generator
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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.

Loading history...
130
    {
131 8
        foreach ($traversable as $key => $value) {
132 6
            yield $key => $value;
133 5
            $this->iteratorAdvanced = true;
134
        }
135
136 4
        $this->iterator = null;
137 4
    }
138
}
139