Completed
Push — master ( d949a7...15ba37 )
by Andreas
15s queued 12s
created

MongoCursor::tailable()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 1
dl 0
loc 7
rs 10
c 0
b 0
f 0
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
if (class_exists('MongoCursor', false)) {
17
    return;
18
}
19
20
use Alcaeus\MongoDbAdapter\AbstractCursor;
21
use Alcaeus\MongoDbAdapter\CursorIterator;
22
use Alcaeus\MongoDbAdapter\TypeConverter;
23
use Alcaeus\MongoDbAdapter\ExceptionConverter;
24
use MongoDB\Driver\Cursor;
25
use MongoDB\Driver\ReadPreference;
26
use MongoDB\Operation\Find;
27
28
/**
29
 * Result object for database query.
30
 * @link http://www.php.net/manual/en/class.mongocursor.php
31
 */
32
class MongoCursor extends AbstractCursor implements Iterator, Countable, MongoCursorInterface
33
{
34
    /**
35
     * @var bool
36
     */
37
    public static $slaveOkay = false;
38
39
    /**
40
     * @var int
41
     */
42
    public static $timeout = 30000;
43
44
    /**
45
     * @var array
46
     */
47
    protected $optionNames = [
48
        'allowPartialResults',
49
        'batchSize',
50
        'cursorType',
51
        'limit',
52
        'maxTimeMS',
53
        'modifiers',
54
        'noCursorTimeout',
55
        'projection',
56
        'readPreference',
57
        'skip',
58
        'sort',
59
    ];
60
61
    /**
62
     * @var array
63
     */
64
    protected $projection;
65
66
    /**
67
     * @var array
68
     */
69
    protected $query;
70
71
    protected $allowPartialResults;
72
    protected $awaitData;
73
    protected $flags = 0;
74
    protected $hint;
75
    protected $limit;
76
    protected $maxTimeMS;
77
    protected $noCursorTimeout;
78
    protected $options = [];
79
    protected $skip;
80
    protected $snapshot;
81
    protected $sort;
82
    protected $tailable;
83
84
    /**
85
     * Create a new cursor
86
     * @link http://www.php.net/manual/en/mongocursor.construct.php
87
     * @param MongoClient $connection Database connection.
88
     * @param string $ns Full name of database and collection.
89
     * @param array $query Database query.
90
     * @param array $fields Fields to return.
91
     */
92
    public function __construct(MongoClient $connection, $ns, array $query = array(), array $fields = array())
93
    {
94
        parent::__construct($connection, $ns);
95
96
        $this->query = $query;
97
        $this->projection = $fields;
98
    }
99
100
    /**
101
     * Adds a top-level key/value pair to a query
102
     * @link http://www.php.net/manual/en/mongocursor.addoption.php
103
     * @param string $key Fieldname to add.
104
     * @param mixed $value Value to add.
105
     * @throws MongoCursorException
106
     * @return MongoCursor Returns this cursor
107
     */
108
    public function addOption($key, $value)
109
    {
110
        $this->errorIfOpened();
111
        $this->options[$key] = $value;
112
113
        return $this;
114
    }
115
116
    /**
117
     * (PECL mongo &gt;= 1.2.11)<br/>
118
     * Sets whether this cursor will wait for a while for a tailable cursor to return more data
119
     * @param bool $wait [optional] <p>If the cursor should wait for more data to become available.</p>
120
     * @return MongoCursor Returns this cursor.
121
     */
122
    public function awaitData($wait = true)
123
    {
124
        $this->errorIfOpened();
125
        $this->awaitData = $wait;
126
127
        return $this;
128
    }
129
130
131
    /**
132
     * Counts the number of results for this query
133
     * @link http://www.php.net/manual/en/mongocursor.count.php
134
     * @param bool $foundOnly Send cursor limit and skip information to the count function, if applicable.
135
     * @return int The number of documents returned by this cursor's query.
136
     */
137
    public function count($foundOnly = false)
138
    {
139
        $optionNames = ['hint', 'maxTimeMS'];
140
        if ($foundOnly) {
141
            $optionNames = array_merge($optionNames, ['limit', 'skip']);
142
        }
143
144
        $options = $this->getOptions($optionNames) + $this->options;
145
        try {
146
            $count = $this->collection->count(TypeConverter::fromLegacy($this->query), $options);
0 ignored issues
show
Deprecated Code introduced by
The method MongoDB\Collection::count() has been deprecated with message: 1.4

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
147
        } catch (\MongoDB\Driver\Exception\ExecutionTimeoutException $e) {
148
            throw new MongoCursorTimeoutException($e->getMessage(), $e->getCode(), $e);
149
        } catch (\MongoDB\Driver\Exception\Exception $e) {
150
            throw ExceptionConverter::toLegacy($e);
151
        }
152
153
        return $count;
154
    }
155
156
    /**
157
     * Execute the query
158
     * @link http://www.php.net/manual/en/mongocursor.doquery.php
159
     * @throws MongoConnectionException if it cannot reach the database.
160
     * @return void
161
     */
162
    protected function doQuery()
163
    {
164
        $options = $this->getOptions() + $this->options;
165
166
        try {
167
            $this->cursor = $this->collection->find(TypeConverter::fromLegacy($this->query), $options);
168
        } catch (\MongoDB\Driver\Exception\ExecutionTimeoutException $e) {
169
            throw new MongoCursorTimeoutException($e->getMessage(), $e->getCode(), $e);
170
        } catch (\MongoDB\Driver\Exception\Exception $e) {
171
            throw ExceptionConverter::toLegacy($e);
172
        }
173
    }
174
175
    /**
176
     * Return an explanation of the query, often useful for optimization and debugging
177
     * @link http://www.php.net/manual/en/mongocursor.explain.php
178
     * @return array Returns an explanation of the query.
179
     */
180
    public function explain()
181
    {
182
        $optionNames = [
183
            'allowPartialResults',
184
            'batchSize',
185
            'cursorType',
186
            'limit',
187
            'maxTimeMS',
188
            'noCursorTimeout',
189
            'projection',
190
            'skip',
191
            'sort',
192
        ];
193
194
        $options = $this->getOptions($optionNames);
195
196
        $command = [
197
            'explain' => [
198
                'find' => $this->collection->getCollectionName(),
199
                'filter' => TypeConverter::fromLegacy($this->query),
200
            ] + $options,
201
        ];
202
203
        $explained = TypeConverter::toLegacy(iterator_to_array($this->db->command($command))[0]);
204
        unset($explained['ok']);
205
206
        return $explained;
207
    }
208
209
    /**
210
     * Sets the fields for a query
211
     * @link http://www.php.net/manual/en/mongocursor.fields.php
212
     * @param array $f Fields to return (or not return).
213
     * @throws MongoCursorException
214
     * @return MongoCursor
215
     */
216
    public function fields(array $f)
217
    {
218
        $this->errorIfOpened();
219
        $this->projection = $f;
220
221
        return $this;
222
    }
223
224
    /**
225
     * Advances the cursor to the next result, and returns that result
226
     * @link http://www.php.net/manual/en/mongocursor.getnext.php
227
     * @throws MongoConnectionException
228
     * @throws MongoCursorTimeoutException
229
     * @return array Returns the next object
230
     */
231
    public function getNext()
232
    {
233
        return $this->next();
234
    }
235
236
    /**
237
     * Checks if there are any more elements in this cursor
238
     * @link http://www.php.net/manual/en/mongocursor.hasnext.php
239
     * @throws MongoConnectionException
240
     * @throws MongoCursorTimeoutException
241
     * @return bool Returns true if there is another element
242
     */
243
    public function hasNext()
244
    {
245 View Code Duplication
        if (! $this->startedIterating) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
246
            $this->ensureIterator();
247
            $this->startedIterating = true;
248
            $this->storeIteratorState();
249
            $this->cursorNeedsAdvancing = false;
250
        } elseif ($this->cursorNeedsAdvancing) {
251
            $this->ensureIterator()->next();
252
            $this->cursorNeedsAdvancing = false;
253
        }
254
255
        return $this->ensureIterator()->valid();
256
    }
257
258
    /**
259
     * Gives the database a hint about the query
260
     * @link http://www.php.net/manual/en/mongocursor.hint.php
261
     * @param array|string $keyPattern Indexes to use for the query.
262
     * @throws MongoCursorException
263
     * @return MongoCursor Returns this cursor
264
     */
265
    public function hint($keyPattern)
266
    {
267
        $this->errorIfOpened();
268
        $this->hint = $keyPattern;
269
270
        return $this;
271
    }
272
273
    /**
274
     * Sets whether this cursor will timeout
275
     * @link http://www.php.net/manual/en/mongocursor.immortal.php
276
     * @param bool $liveForever If the cursor should be immortal.
277
     * @throws MongoCursorException
278
     * @return MongoCursor Returns this cursor
279
     */
280
    public function immortal($liveForever = true)
281
    {
282
        $this->errorIfOpened();
283
        $this->noCursorTimeout = $liveForever;
284
285
        return $this;
286
    }
287
288
    /**
289
     * Limits the number of results returned
290
     * @link http://www.php.net/manual/en/mongocursor.limit.php
291
     * @param int $num The number of results to return.
292
     * @throws MongoCursorException
293
     * @return MongoCursor Returns this cursor
294
     */
295
    public function limit($num)
296
    {
297
        $this->errorIfOpened();
298
        $this->limit = $num;
299
300
        return $this;
301
    }
302
303
    /**
304
     * @param int $ms
305
     * @return $this
306
     * @throws MongoCursorException
307
     */
308
    public function maxTimeMS($ms)
309
    {
310
        $this->errorIfOpened();
311
        $this->maxTimeMS = $ms;
312
313
        return $this;
314
    }
315
316
    /**
317
     * @link http://www.php.net/manual/en/mongocursor.partial.php
318
     * @param bool $okay [optional] <p>If receiving partial results is okay.</p>
319
     * @return MongoCursor Returns this cursor.
320
     */
321
    public function partial($okay = true)
322
    {
323
        $this->allowPartialResults = $okay;
324
325
        return $this;
326
    }
327
328
    /**
329
     * Clears the cursor
330
     * @link http://www.php.net/manual/en/mongocursor.reset.php
331
     * @return void
332
     */
333
    public function reset()
334
    {
335
        parent::reset();
336
    }
337
338
    /**
339
     * @link http://www.php.net/manual/en/mongocursor.setflag.php
340
     * @param int $flag
341
     * @param bool $set
342
     * @return MongoCursor
343
     */
344
    public function setFlag($flag, $set = true)
0 ignored issues
show
Unused Code introduced by
The parameter $flag 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...
Unused Code introduced by
The parameter $set 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...
345
    {
346
        $this->notImplemented();
347
    }
348
349
    /**
350
     * Skips a number of results
351
     * @link http://www.php.net/manual/en/mongocursor.skip.php
352
     * @param int $num The number of results to skip.
353
     * @throws MongoCursorException
354
     * @return MongoCursor Returns this cursor
355
     */
356
    public function skip($num)
357
    {
358
        $this->errorIfOpened();
359
        $this->skip = $num;
360
361
        return $this;
362
    }
363
364
    /**
365
     * Sets whether this query can be done on a slave
366
     * This method will override the static class variable slaveOkay.
367
     * @link http://www.php.net/manual/en/mongocursor.slaveOkay.php
368
     * @param boolean $okay If it is okay to query the slave.
369
     * @throws MongoCursorException
370
     * @return MongoCursor Returns this cursor
371
     */
372
    public function slaveOkay($okay = true)
373
    {
374
        $this->errorIfOpened();
375
376
        $this->setReadPreferenceFromSlaveOkay($okay);
377
378
        return $this;
379
    }
380
381
    /**
382
     * Use snapshot mode for the query
383
     * @link http://www.php.net/manual/en/mongocursor.snapshot.php
384
     * @throws MongoCursorException
385
     * @return MongoCursor Returns this cursor
386
     */
387
    public function snapshot()
388
    {
389
        $this->errorIfOpened();
390
        $this->snapshot = true;
391
392
        return $this;
393
    }
394
395
    /**
396
     * Sorts the results by given fields
397
     * @link http://www.php.net/manual/en/mongocursor.sort.php
398
     * @param array $fields An array of fields by which to sort. Each element in the array has as key the field name, and as value either 1 for ascending sort, or -1 for descending sort
399
     * @throws MongoCursorException
400
     * @return MongoCursor Returns the same cursor that this method was called on
401
     */
402
    public function sort(array $fields)
403
    {
404
        $this->errorIfOpened();
405
        $this->sort = $fields;
406
407
        return $this;
408
    }
409
410
    /**
411
     * Sets whether this cursor will be left open after fetching the last results
412
     * @link http://www.php.net/manual/en/mongocursor.tailable.php
413
     * @param bool $tail If the cursor should be tailable.
414
     * @return MongoCursor Returns this cursor
415
     */
416
    public function tailable($tail = true)
417
    {
418
        $this->errorIfOpened();
419
        $this->tailable = $tail;
420
421
        return $this;
422
    }
423
424
    /**
425
     * @return int|null
426
     */
427
    protected function convertCursorType()
428
    {
429
        if (! $this->tailable) {
430
            return null;
431
        }
432
433
        return $this->awaitData ? Find::TAILABLE_AWAIT : Find::TAILABLE;
434
    }
435
436
    /**
437
     * @return array
438
     */
439
    protected function convertModifiers()
440
    {
441
        $modifiers = array_key_exists('modifiers', $this->options) ? $this->options['modifiers'] : [];
442
443
        foreach (['hint', 'snapshot'] as $modifier) {
444
            if ($this->$modifier === null) {
445
                continue;
446
            }
447
448
            $modifiers['$' . $modifier] = $this->$modifier;
449
        }
450
451
        return $modifiers;
452
    }
453
454
    /**
455
     * @return array
456
     */
457
    protected function convertProjection()
458
    {
459
        return TypeConverter::convertProjection($this->projection);
460
    }
461
462
    /**
463
     * @return Cursor
464
     */
465
    protected function ensureCursor()
466
    {
467
        if ($this->cursor === null) {
468
            $this->doQuery();
469
        }
470
471
        return $this->cursor;
472
    }
473
474
    /**
475
     * @param \Traversable $traversable
476
     * @return CursorIterator
477
     */
478
    protected function wrapTraversable(\Traversable $traversable)
479
    {
480
        return new CursorIterator($traversable, true);
481
    }
482
483
    /**
484
     * @return array
485
     */
486
    protected function getCursorInfo()
487
    {
488
        return [
489
            'ns' => $this->ns,
490
            'limit' => $this->limit,
491
            'batchSize' => (int) $this->batchSize,
492
            'skip' => $this->skip,
493
            'flags' => $this->flags,
494
            'query' => $this->query,
495
            'fields' => $this->projection,
496
        ];
497
    }
498
499
    /**
500
     * @return array
501
     */
502
    public function __sleep()
503
    {
504
        return [
505
            'allowPartialResults',
506
            'awaitData',
507
            'flags',
508
            'hint',
509
            'limit',
510
            'maxTimeMS',
511
            'noCursorTimeout',
512
            'optionNames',
513
            'options',
514
            'projection',
515
            'query',
516
            'skip',
517
            'snapshot',
518
            'sort',
519
            'tailable',
520
        ] + parent::__sleep();
521
    }
522
}
523