Completed
Push — master ( c56abb...8c66c1 )
by Andreas
03:48
created

AbstractCursor::batchSize()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 6
ccs 3
cts 3
cp 1
rs 9.4285
cc 1
eloc 3
nc 1
nop 1
crap 1
1
<?php
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
16
namespace Alcaeus\MongoDbAdapter;
17
18
use Alcaeus\MongoDbAdapter\Helper\ReadPreference;
19
use MongoDB\Collection;
20
use MongoDB\Driver\Cursor;
21
22
/**
23
 * @internal
24
 */
25
abstract class AbstractCursor
26
{
27
    use ReadPreference;
28
29
    /**
30
     * @var int
31
     */
32
    protected $batchSize;
33
34
    /**
35
     * @var Collection
36
     */
37
    protected $collection;
38
39
    /**
40
     * @var \MongoClient
41
     */
42
    protected $connection;
43
44
    /**
45
     * @var Cursor
46
     */
47
    protected $cursor;
48
49
    /**
50
     * @var \MongoDB\Database
51
     */
52
    protected $db;
53
54
    /**
55
     * @var \IteratorIterator
56
     */
57
    protected $iterator;
58
59
    /**
60
     * @var string
61
     */
62
    protected $ns;
63
64
    /**
65
     * @var bool
66
     */
67
    protected $startedIterating = false;
68
69
    /**
70
     * @var array
71
     */
72
    protected $optionNames = [
73
        'batchSize',
74
        'readPreference',
75
    ];
76
77
    /**
78
     * @return Cursor
79
     */
80
    abstract protected function ensureCursor();
81
82
    /**
83
     * @return array
84
     */
85
    abstract protected function getCursorInfo();
86
87
    /**
88
     * Create a new cursor
89
     * @link http://www.php.net/manual/en/mongocursor.construct.php
90
     * @param \MongoClient $connection Database connection.
91
     * @param string $ns Full name of database and collection.
92
     */
93 42
    public function __construct(\MongoClient $connection, $ns)
94
    {
95 42
        $this->connection = $connection;
96 42
        $this->ns = $ns;
97
98 42
        $nsParts = explode('.', $ns);
99 42
        $dbName = array_shift($nsParts);
100 42
        $collectionName = implode('.', $nsParts);
101
102 42
        $this->db = $connection->selectDB($dbName)->getDb();
103
104 42
        if ($collectionName) {
105 34
            $this->collection = $connection->selectCollection($dbName, $collectionName)->getCollection();
106
        }
107 42
    }
108
109
    /**
110
     * Returns the current element
111
     * @link http://www.php.net/manual/en/mongocursor.current.php
112
     * @return array
113
     */
114 19
    public function current()
115
    {
116 19
        $this->startedIterating = true;
117 19
        $document = $this->ensureIterator()->current();
118 19
        if ($document !== null) {
119 19
            $document = TypeConverter::toLegacy($document);
120
        }
121
122 19
        return $document;
123
    }
124
125
    /**
126
     * Returns the current result's _id
127
     * @link http://www.php.net/manual/en/mongocursor.key.php
128
     * @return string The current result's _id as a string.
129
     */
130 10
    public function key()
131
    {
132 10
        return $this->ensureIterator()->key();
133
    }
134
135
    /**
136
     * Advances the cursor to the next result, and returns that result
137
     * @link http://www.php.net/manual/en/mongocursor.next.php
138
     * @throws \MongoConnectionException
139
     * @throws \MongoCursorTimeoutException
140
     * @return array Returns the next object
141
     */
142 19
    public function next()
143
    {
144 19
        if (!$this->startedIterating) {
145 1
            $this->ensureIterator();
146 1
            $this->startedIterating = true;
147
        } else {
148 19
            $this->ensureIterator()->next();
149
        }
150
151 19
        return $this->current();
152
    }
153
154
    /**
155
     * Returns the cursor to the beginning of the result set
156
     * @throws \MongoConnectionException
157
     * @throws \MongoCursorTimeoutException
158
     * @return void
159
     */
160 36
    public function rewind()
161
    {
162
        // We can recreate the cursor to allow it to be rewound
163 36
        $this->reset();
164 36
        $this->startedIterating = true;
165 36
        $this->ensureIterator()->rewind();
166 34
    }
167
168
    /**
169
     * Checks if the cursor is reading a valid result.
170
     * @link http://www.php.net/manual/en/mongocursor.valid.php
171
     * @return boolean If the current result is not null.
172
     */
173 34
    public function valid()
174
    {
175 34
        return $this->ensureIterator()->valid();
176
    }
177
178
    /**
179
     * Limits the number of elements returned in one batch.
180
     *
181
     * @link http://docs.php.net/manual/en/mongocursor.batchsize.php
182
     * @param int $batchSize The number of results to return per batch
183
     * @return $this Returns this cursor.
184
     */
185 1
    public function batchSize($batchSize)
186
    {
187 1
        $this->batchSize = $batchSize;
188
189 1
        return $this;
190
    }
191
192
    /**
193
     * Checks if there are documents that have not been sent yet from the database for this cursor
194
     * @link http://www.php.net/manual/en/mongocursor.dead.php
195
     * @return boolean Returns if there are more results that have not been sent to the client, yet.
196
     */
197
    public function dead()
198
    {
199
        return $this->ensureCursor()->isDead();
200
    }
201
202
    /**
203
     * @return array
204
     */
205 2
    public function info()
206
    {
207 2
        return $this->getCursorInfo() + $this->getIterationInfo();
208
    }
209
210
    /**
211
     * @link http://www.php.net/manual/en/mongocursor.setreadpreference.php
212
     * @param string $readPreference
213
     * @param array $tags
214
     * @return $this Returns this cursor.
215
     */
216 42
    public function setReadPreference($readPreference, $tags = null)
217
    {
218 42
        $this->setReadPreferenceFromParameters($readPreference, $tags);
219
220 42
        return $this;
221
    }
222
223
    /**
224
     * Sets a client-side timeout for this query
225
     * @link http://www.php.net/manual/en/mongocursor.timeout.php
226
     * @param int $ms The number of milliseconds for the cursor to wait for a response. By default, the cursor will wait forever.
227
     * @return $this Returns this cursor
228
     */
229
    public function timeout($ms)
0 ignored issues
show
Unused Code introduced by
The parameter $ms is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
230
    {
231
        $this->notImplemented();
232
    }
233
234
    /**
235
     * Applies all options set on the cursor, overwriting any options that have already been set
236
     *
237
     * @param array $optionNames Array of option names to be applied (will be read from properties)
238
     * @return array
239
     */
240 39
    protected function getOptions($optionNames = null)
241
    {
242 39
        $options = [];
243
244 39
        if ($optionNames === null) {
245 37
            $optionNames = $this->optionNames;
246
        }
247
248 39
        foreach ($optionNames as $option) {
249 39
            $converter = 'convert' . ucfirst($option);
250 39
            $value = method_exists($this, $converter) ? $this->$converter() : $this->$option;
251
252 39
            if ($value === null) {
253 39
                continue;
254
            }
255
256 38
            $options[$option] = $value;
257
        }
258
259 39
        return $options;
260
    }
261
262
    /**
263
     * @return \Generator
264
     */
265 37
    protected function ensureIterator()
266
    {
267 37
        if ($this->iterator === null) {
268
            // MongoDB\Driver\Cursor needs to be wrapped into a \Generator so that a valid \Iterator with working implementations of
269
            // next, current, valid, key and rewind is returned. These methods don't work if we wrap the Cursor inside an \IteratorIterator
270 37
            $this->iterator = $this->wrapTraversable($this->ensureCursor());
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->wrapTraversable($this->ensureCursor()) of type object<Generator> is incompatible with the declared type object<IteratorIterator> of property $iterator.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
271
        }
272
273 35
        return $this->iterator;
0 ignored issues
show
Bug Compatibility introduced by
The expression $this->iterator; of type Generator|IteratorIterator adds the type IteratorIterator to the return on line 273 which is incompatible with the return type documented by Alcaeus\MongoDbAdapter\A...tCursor::ensureIterator of type Generator.
Loading history...
274
    }
275
276
    /**
277
     * @param \Traversable $traversable
278
     * @return \Generator
279
     */
280 8
    protected function wrapTraversable(\Traversable $traversable)
281
    {
282 8
        foreach ($traversable as $key => $value) {
283 8
            yield $key => $value;
284
        }
285 8
    }
286
287
    /**
288
     * @throws \MongoCursorException
289
     */
290 15
    protected function errorIfOpened()
291
    {
292 15
        if ($this->cursor === null) {
293 15
            return;
294
        }
295
296
        throw new \MongoCursorException('cannot modify cursor after beginning iteration.');
297
    }
298
299
    /**
300
     * @return array
301
     */
302 2
    protected function getIterationInfo()
303
    {
304
        $iterationInfo = [
305 2
            'started_iterating' => $this->cursor !== null,
306
        ];
307
308 2
        if ($this->cursor !== null) {
309 2
            switch ($this->cursor->getServer()->getType()) {
310
                case \MongoDB\Driver\Server::TYPE_RS_ARBITER:
311
                    $typeString = 'ARBITER';
312
                    break;
313
                case \MongoDB\Driver\Server::TYPE_MONGOS:
314
                    $typeString = 'MONGOS';
315
                    break;
316
                case \MongoDB\Driver\Server::TYPE_RS_PRIMARY:
317
                    $typeString = 'PRIMARY';
318
                    break;
319 2
                case \MongoDB\Driver\Server::TYPE_RS_SECONDARY:
320
                    $typeString = 'SECONDARY';
321
                    break;
322
                default:
323 2
                    $typeString = 'STANDALONE';
324
            }
325
326
            $iterationInfo += [
327 2
                'id' => (string) $this->cursor->getId(),
328
                'at' => null, // @todo Complete info for cursor that is iterating
329
                'numReturned' => null, // @todo Complete info for cursor that is iterating
330
                'server' => null, // @todo Complete info for cursor that is iterating
331 2
                'host' => $this->cursor->getServer()->getHost(),
332 2
                'port' => $this->cursor->getServer()->getPort(),
333 2
                'connection_type_desc' => $typeString,
334
            ];
335
        }
336
337 2
        return $iterationInfo;
338
    }
339
340
    /**
341
     * @throws \Exception
342
     */
343
    protected function notImplemented()
344
    {
345
        throw new \Exception('Not implemented');
346
    }
347
348
    /**
349
     * Clears the cursor
350
     *
351
     * This is generic but implemented as protected since it's only exposed in MongoCursor
352
     */
353 37
    protected function reset()
354
    {
355 37
        $this->startedIterating = false;
356 37
        $this->cursor = null;
357 37
        $this->iterator = null;
358 37
    }
359
}
360