Completed
Push — master ( 11f27c...4713a5 )
by Andreas
08:33
created

CachingIterator::storeCurrentItem()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 10
ccs 6
cts 6
cp 1
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 5
nc 2
nop 0
crap 2
1
<?php declare(strict_types = 1);
2
/*
3
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
4
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
5
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
6
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
7
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
8
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
9
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
10
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
11
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
12
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
13
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
14
 *
15
 * This software consists of voluntary contributions made by many individuals
16
 * and is licensed under the MIT license. For more information, see
17
 * <http://www.doctrine-project.org>.
18
 */
19
20
namespace Doctrine\ODM\MongoDB\Iterator;
21
22
/**
23
 * Iterator for wrapping a Traversable and caching its results.
24
 *
25
 * By caching results, this iterators allows a Traversable to be counted and
26
 * rewound multiple times, even if the wrapped object does not natively support
27
 * those operations (e.g. MongoDB\Driver\Cursor).
28
 *
29
 * @internal
30
 */
31
final class CachingIterator implements Iterator
32
{
33
    private $items = [];
34
    private $iterator;
35
    private $iteratorAdvanced = false;
36
    private $iteratorExhausted = false;
37
38
    /**
39
     * Constructor.
40
     *
41
     * Initialize the iterator and stores the first item in the cache. This
42
     * effectively rewinds the Traversable and the wrapping Generator, which
43
     * will execute up to its first yield statement. Additionally, this mimics
44
     * behavior of the SPL iterators and allows users to omit an explicit call
45
     * to rewind() before using the other methods.
46
     *
47
     * @param Traversable $iterator
48
     */
49 141
    public function __construct(\Traversable $iterator)
50
    {
51 141
        $this->iterator = $this->wrapTraversable($iterator);
52 141
        $this->storeCurrentItem();
53 141
    }
54
55 80
    public function toArray(): array
56
    {
57 80
        $this->exhaustIterator();
58
59 80
        return $this->items;
60
    }
61
62
    /**
63
     * @see http://php.net/iterator.current
64
     * @return mixed
65
     */
66 90
    public function current()
67
    {
68 90
        return current($this->items);
69
    }
70
71
    /**
72
     * @see http://php.net/iterator.mixed
73
     * @return mixed
74
     */
75 33
    public function key()
76
    {
77 33
        return key($this->items);
78
    }
79
80
    /**
81
     * @see http://php.net/iterator.next
82
     * @return void
83
     */
84 82
    public function next()
85
    {
86 82
        if ( ! $this->iteratorExhausted) {
87 82
            $this->iterator->next();
88 82
            $this->storeCurrentItem();
89
        }
90
91 82
        next($this->items);
92 82
    }
93
94
    /**
95
     * @see http://php.net/iterator.rewind
96
     * @return void
97
     */
98 29
    public function rewind()
99
    {
100
        /* If the iterator has advanced, exhaust it now so that future iteration
101
         * can rely on the cache.
102
         */
103 29
        if ($this->iteratorAdvanced) {
104 20
            $this->exhaustIterator();
105
        }
106
107 29
        reset($this->items);
108 29
    }
109
110
    /**
111
     * 
112
     * @see http://php.net/iterator.valid
113
     * @return boolean
114
     */
115 33
    public function valid()
116
    {
117 33
        return $this->key() !== null;
118
    }
119
120
    /**
121
     * Ensures that the inner iterator is fully consumed and cached.
122
     */
123 84
    private function exhaustIterator()
124
    {
125 84
        while ( ! $this->iteratorExhausted) {
126 73
            $this->next();
127
        }
128 84
    }
129
130
    /**
131
     * Stores the current item in the cache.
132
     */
133 141
    private function storeCurrentItem()
134
    {
135 141
        $key = $this->iterator->key();
136
137 141
        if ($key === null) {
138 92
            return;
139
        }
140
141 132
        $this->items[$key] = $this->iterator->current();
142 132
    }
143
144 141
    private function wrapTraversable(\Traversable $traversable): \Generator
145
    {
146 141
        foreach ($traversable as $key => $value) {
147 132
            yield $key => $value;
148 82
            $this->iteratorAdvanced = true;
149
        }
150
151 92
        $this->iteratorExhausted = true;
152 92
    }
153
}
154