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

CachingIterator   A

Complexity

Total Complexity 15

Size/Duplication

Total Lines 123
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 0

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
wmc 15
lcom 1
cbo 0
dl 0
loc 123
ccs 40
cts 40
cp 1
rs 10
c 0
b 0
f 0

10 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 5 1
A toArray() 0 6 1
A current() 0 4 1
A key() 0 4 1
A next() 0 9 2
A rewind() 0 11 2
A valid() 0 4 1
A exhaustIterator() 0 6 2
A storeCurrentItem() 0 10 2
A wrapTraversable() 0 9 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