Skip to content

Commit 0d5b7c4

Browse files
committed
PHPLIB-81: Move CachingIterator to MongoDB\Model
1 parent 9f67730 commit 0d5b7c4

File tree

3 files changed

+160
-126
lines changed

3 files changed

+160
-126
lines changed

src/CachingIterator.php

Lines changed: 0 additions & 119 deletions
This file was deleted.

src/Model/CachingIterator.php

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
<?php
2+
/*
3+
* Copyright 2017 MongoDB, Inc.
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
namespace MongoDB\Model;
19+
20+
use Countable;
21+
use Generator;
22+
use Iterator;
23+
use Traversable;
24+
25+
/**
26+
* Iterator for wrapping a Traversable and caching its results.
27+
*
28+
* By caching results, this iterators allows a Traversable to be counted and
29+
* rewound multiple times, even if the wrapped object does not natively support
30+
* those operations (e.g. MongoDB\Driver\Cursor).
31+
*
32+
* @internal
33+
*/
34+
class CachingIterator implements Countable, Iterator
35+
{
36+
private $items;
37+
private $iterator;
38+
private $iteratorExhausted = false;
39+
40+
/**
41+
* @param Traversable $traversable
42+
*/
43+
public function __construct(Traversable $traversable)
44+
{
45+
$this->iterator = $this->wrapTraversable($traversable);
46+
$this->storeCurrentItem();
47+
}
48+
49+
/**
50+
* @see http://php.net/countable.count
51+
* @return integer
52+
*/
53+
public function count()
54+
{
55+
$this->exhaustIterator();
56+
57+
return count($this->items);
58+
}
59+
60+
/**
61+
* @see http://php.net/iterator.current
62+
* @return mixed
63+
*/
64+
public function current()
65+
{
66+
return current($this->items);
67+
}
68+
69+
/**
70+
* @see http://php.net/iterator.mixed
71+
* @return mixed
72+
*/
73+
public function key()
74+
{
75+
return key($this->items);
76+
}
77+
78+
/**
79+
* @see http://php.net/iterator.next
80+
* @return void
81+
*/
82+
public function next()
83+
{
84+
if ( ! $this->iteratorExhausted) {
85+
$this->iterator->next();
86+
$this->storeCurrentItem();
87+
}
88+
89+
next($this->items);
90+
}
91+
92+
/**
93+
* @see http://php.net/iterator.rewind
94+
* @return void
95+
*/
96+
public function rewind()
97+
{
98+
$this->exhaustIterator();
99+
reset($this->items);
100+
}
101+
102+
/**
103+
*
104+
* @see http://php.net/iterator.valid
105+
* @return boolean
106+
*/
107+
public function valid()
108+
{
109+
return $this->key() !== null;
110+
}
111+
112+
/**
113+
* Ensures that the inner iterator is fully consumed and cached.
114+
*/
115+
private function exhaustIterator()
116+
{
117+
while ( ! $this->iteratorExhausted) {
118+
$this->next();
119+
}
120+
}
121+
122+
/**
123+
* Stores the current item in the cache.
124+
*/
125+
private function storeCurrentItem()
126+
{
127+
$key = $this->iterator->key();
128+
129+
if ($key === null) {
130+
return;
131+
}
132+
133+
$this->items[$key] = $this->iterator->current();
134+
}
135+
136+
/**
137+
* Wraps the Traversable with a Generator.
138+
*
139+
* @param Traversable $traversable
140+
* @return Generator
141+
*/
142+
private function wrapTraversable(Traversable $traversable)
143+
{
144+
foreach ($traversable as $key => $value) {
145+
yield $key => $value;
146+
}
147+
148+
$this->iteratorExhausted = true;
149+
}
150+
}

tests/CachingIteratorTest.php renamed to tests/Model/CachingIteratorTest.php

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,40 @@
11
<?php
22

3-
namespace MongoDB\Tests;
3+
namespace MongoDB\Tests\Model;
44

5-
use MongoDB\CachingIterator;
5+
use MongoDB\Model\CachingIterator;
66

77
class CachingIteratorTest extends \PHPUnit_Framework_TestCase
88
{
99
/**
10-
* Sanity check for all following tests
10+
* Sanity check for all following tests.
11+
*
1112
* @expectedException \Exception
1213
* @expectedExceptionMessage Cannot traverse an already closed generator
1314
*/
14-
public function testTraverseGeneratorConsumesIt()
15+
public function testTraversingGeneratorConsumesIt()
1516
{
1617
$iterator = $this->getTraversable([1, 2, 3]);
1718
$this->assertSame([1, 2, 3], iterator_to_array($iterator));
1819
$this->assertSame([1, 2, 3], iterator_to_array($iterator));
1920
}
2021

21-
public function testIterateOverItems()
22+
public function testIteration()
2223
{
2324
$iterator = new CachingIterator($this->getTraversable([1, 2, 3]));
2425

2526
$expectedKey = 0;
2627
$expectedItem = 1;
28+
2729
foreach ($iterator as $key => $item) {
2830
$this->assertSame($expectedKey++, $key);
2931
$this->assertSame($expectedItem++, $item);
3032
}
33+
3134
$this->assertFalse($iterator->valid());
3235
}
3336

34-
public function testIteratePartiallyThenRewind()
37+
public function testRewindAfterPartialIteration()
3538
{
3639
$iterator = new CachingIterator($this->getTraversable([1, 2, 3]));
3740

@@ -47,7 +50,7 @@ public function testCount()
4750
$this->assertCount(3, $iterator);
4851
}
4952

50-
public function testCountAfterPartiallyIterating()
53+
public function testCountAfterPartialIteration()
5154
{
5255
$iterator = new CachingIterator($this->getTraversable([1, 2, 3]));
5356
$iterator->next();

0 commit comments

Comments
 (0)