Completed
Push — master ( 86a25f...30ae90 )
by Andreas
25:40
created

MongoCursor::convertProjection()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 4
rs 10
cc 1
eloc 2
nc 1
nop 0
1
<?php
1 ignored issue
show
Coding Style Compatibility introduced by
For compatibility and reusability of your code, PSR1 recommends that a file should introduce either new symbols (like classes, functions, etc.) or have side-effects (like outputting something, or including other files), but not both at the same time. The first symbol is defined on line 31 and the first side effect is on line 17.

The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.

The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.

To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.

Loading history...
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\TypeConverter;
22
use Alcaeus\MongoDbAdapter\ExceptionConverter;
23
use MongoDB\Driver\Cursor;
24
use MongoDB\Driver\ReadPreference;
25
use MongoDB\Operation\Find;
26
27
/**
28
 * Result object for database query.
29
 * @link http://www.php.net/manual/en/class.mongocursor.php
30
 */
31
class MongoCursor extends AbstractCursor implements Iterator
32
{
33
    /**
34
     * @var bool
35
     */
36
    public static $slaveOkay = false;
37
38
    /**
39
     * @var int
40
     */
41
    public static $timeout = 30000;
42
43
    /**
44
     * @var array
45
     */
46
    protected $optionNames = [
47
        'allowPartialResults',
48
        'batchSize',
49
        'cursorType',
50
        'limit',
51
        'maxTimeMS',
52
        'modifiers',
53
        'noCursorTimeout',
54
        'projection',
55
        'readPreference',
56
        'skip',
57
        'sort',
58
    ];
59
60
    /**
61
     * @var array
62
     */
63
    protected $projection;
64
65
    /**
66
     * @var array
67
     */
68
    protected $query;
69
70
    protected $allowPartialResults;
71
    protected $awaitData;
72
    protected $flags = 0;
73
    protected $hint;
74
    protected $limit;
75
    protected $maxTimeMS;
76
    protected $noCursorTimeout;
77
    protected $options = [];
78
    protected $skip;
79
    protected $snapshot;
80
    protected $sort;
81
    protected $tailable;
82
83
    /**
84
     * Create a new cursor
85
     * @link http://www.php.net/manual/en/mongocursor.construct.php
86
     * @param MongoClient $connection Database connection.
87
     * @param string $ns Full name of database and collection.
88
     * @param array $query Database query.
89
     * @param array $fields Fields to return.
90
     */
91
    public function __construct(MongoClient $connection, $ns, array $query = array(), array $fields = array())
92
    {
93
        parent::__construct($connection, $ns);
94
95
        $this->query = $query;
96
        $this->projection = $fields;
97
    }
98
99
    /**
100
     * Adds a top-level key/value pair to a query
101
     * @link http://www.php.net/manual/en/mongocursor.addoption.php
102
     * @param string $key Fieldname to add.
103
     * @param mixed $value Value to add.
104
     * @throws MongoCursorException
105
     * @return MongoCursor Returns this cursor
106
     */
107
    public function addOption($key, $value)
108
    {
109
        $this->errorIfOpened();
110
        $this->options[$key] = $value;
111
112
        return $this;
113
    }
114
115
    /**
116
     * (PECL mongo &gt;= 1.2.11)<br/>
117
     * Sets whether this cursor will wait for a while for a tailable cursor to return more data
118
     * @param bool $wait [optional] <p>If the cursor should wait for more data to become available.</p>
119
     * @return MongoCursor Returns this cursor.
120
     */
121
    public function awaitData($wait = true)
122
    {
123
        $this->errorIfOpened();
124
        $this->awaitData = $wait;
125
126
        return $this;
127
    }
128
129
130
    /**
131
     * Counts the number of results for this query
132
     * @link http://www.php.net/manual/en/mongocursor.count.php
133
     * @param bool $foundOnly Send cursor limit and skip information to the count function, if applicable.
134
     * @return int The number of documents returned by this cursor's query.
135
     */
136
    public function count($foundOnly = false)
137
    {
138
        $optionNames = ['hint', 'maxTimeMS'];
139
        if ($foundOnly) {
140
            $optionNames = array_merge($optionNames, ['limit', 'skip']);
141
        }
142
143
        $options = $this->getOptions($optionNames) + $this->options;
144
        try {
145
            $count = $this->collection->count(TypeConverter::fromLegacy($this->query), $options);
146
        } catch (\MongoDB\Driver\Exception\ExecutionTimeoutException $e) {
147
            throw new MongoCursorTimeoutException($e->getMessage(), $e->getCode(), $e);
148
        } catch (\MongoDB\Driver\Exception\Exception $e) {
149
            throw ExceptionConverter::toLegacy($e);
150
        }
151
152
        return $count;
153
    }
154
155
    /**
156
     * Execute the query
157
     * @link http://www.php.net/manual/en/mongocursor.doquery.php
158
     * @throws MongoConnectionException if it cannot reach the database.
159
     * @return void
160
     */
161
    protected function doQuery()
162
    {
163
        $options = $this->getOptions() + $this->options;
164
165
        try {
166
            $this->cursor = $this->collection->find(TypeConverter::fromLegacy($this->query), $options);
167
        } catch (\MongoDB\Driver\Exception\ExecutionTimeoutException $e) {
168
            throw new MongoCursorTimeoutException($e->getMessage(), $e->getCode(), $e);
169
        } catch (\MongoDB\Driver\Exception\Exception $e) {
170
            throw ExceptionConverter::toLegacy($e);
171
        }
172
    }
173
174
    /**
175
     * Return an explanation of the query, often useful for optimization and debugging
176
     * @link http://www.php.net/manual/en/mongocursor.explain.php
177
     * @return array Returns an explanation of the query.
178
     */
179
    public function explain()
180
    {
181
        $this->notImplemented();
182
    }
183
184
    /**
185
     * Sets the fields for a query
186
     * @link http://www.php.net/manual/en/mongocursor.fields.php
187
     * @param array $f Fields to return (or not return).
188
     * @throws MongoCursorException
189
     * @return MongoCursor
190
     */
191
    public function fields(array $f)
192
    {
193
        $this->errorIfOpened();
194
        $this->projection = $f;
195
196
        return $this;
197
    }
198
199
    /**
200
     * Advances the cursor to the next result, and returns that result
201
     * @link http://www.php.net/manual/en/mongocursor.getnext.php
202
     * @throws MongoConnectionException
203
     * @throws MongoCursorTimeoutException
204
     * @return array Returns the next object
205
     */
206
    public function getNext()
207
    {
208
        return $this->next();
209
    }
210
211
    /**
212
     * Checks if there are any more elements in this cursor
213
     * @link http://www.php.net/manual/en/mongocursor.hasnext.php
214
     * @throws MongoConnectionException
215
     * @throws MongoCursorTimeoutException
216
     * @return bool Returns true if there is another element
217
     */
218
    public function hasNext()
219
    {
220 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...
221
            $this->ensureIterator();
222
            $this->startedIterating = true;
223
            $this->storeIteratorState();
224
            $this->cursorNeedsAdvancing = false;
225
        } elseif ($this->cursorNeedsAdvancing) {
226
            $this->ensureIterator()->next();
227
            $this->cursorNeedsAdvancing = false;
228
        }
229
230
        return $this->ensureIterator()->valid();
231
    }
232
233
    /**
234
     * Gives the database a hint about the query
235
     * @link http://www.php.net/manual/en/mongocursor.hint.php
236
     * @param array|string $keyPattern Indexes to use for the query.
237
     * @throws MongoCursorException
238
     * @return MongoCursor Returns this cursor
239
     */
240
    public function hint($keyPattern)
241
    {
242
        $this->errorIfOpened();
243
        $this->hint = $keyPattern;
244
245
        return $this;
246
    }
247
248
    /**
249
     * Sets whether this cursor will timeout
250
     * @link http://www.php.net/manual/en/mongocursor.immortal.php
251
     * @param bool $liveForever If the cursor should be immortal.
252
     * @throws MongoCursorException
253
     * @return MongoCursor Returns this cursor
254
     */
255
    public function immortal($liveForever = true)
256
    {
257
        $this->errorIfOpened();
258
        $this->noCursorTimeout = $liveForever;
259
260
        return $this;
261
    }
262
263
    /**
264
     * Limits the number of results returned
265
     * @link http://www.php.net/manual/en/mongocursor.limit.php
266
     * @param int $num The number of results to return.
267
     * @throws MongoCursorException
268
     * @return MongoCursor Returns this cursor
269
     */
270
    public function limit($num)
271
    {
272
        $this->errorIfOpened();
273
        $this->limit = $num;
274
275
        return $this;
276
    }
277
278
    /**
279
     * @param int $ms
280
     * @return $this
281
     * @throws MongoCursorException
282
     */
283
    public function maxTimeMS($ms)
284
    {
285
        $this->errorIfOpened();
286
        $this->maxTimeMS = $ms;
287
288
        return $this;
289
    }
290
291
    /**
292
     * @link http://www.php.net/manual/en/mongocursor.partial.php
293
     * @param bool $okay [optional] <p>If receiving partial results is okay.</p>
294
     * @return MongoCursor Returns this cursor.
295
     */
296
    public function partial($okay = true)
297
    {
298
        $this->allowPartialResults = $okay;
299
300
        return $this;
301
    }
302
303
    /**
304
     * Clears the cursor
305
     * @link http://www.php.net/manual/en/mongocursor.reset.php
306
     * @return void
307
     */
308
    public function reset()
309
    {
310
        parent::reset();
311
    }
312
313
    /**
314
     * @link http://www.php.net/manual/en/mongocursor.setflag.php
315
     * @param int $flag
316
     * @param bool $set
317
     * @return MongoCursor
318
     */
319
    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...
320
    {
321
        $this->notImplemented();
322
    }
323
324
    /**
325
     * Skips a number of results
326
     * @link http://www.php.net/manual/en/mongocursor.skip.php
327
     * @param int $num The number of results to skip.
328
     * @throws MongoCursorException
329
     * @return MongoCursor Returns this cursor
330
     */
331
    public function skip($num)
332
    {
333
        $this->errorIfOpened();
334
        $this->skip = $num;
335
336
        return $this;
337
    }
338
339
    /**
340
     * Sets whether this query can be done on a slave
341
     * This method will override the static class variable slaveOkay.
342
     * @link http://www.php.net/manual/en/mongocursor.slaveOkay.php
343
     * @param boolean $okay If it is okay to query the slave.
344
     * @throws MongoCursorException
345
     * @return MongoCursor Returns this cursor
346
     */
347
    public function slaveOkay($okay = true)
348
    {
349
        $this->errorIfOpened();
350
351
        $this->setReadPreferenceFromSlaveOkay($okay);
352
353
        return $this;
354
    }
355
356
    /**
357
     * Use snapshot mode for the query
358
     * @link http://www.php.net/manual/en/mongocursor.snapshot.php
359
     * @throws MongoCursorException
360
     * @return MongoCursor Returns this cursor
361
     */
362
    public function snapshot()
363
    {
364
        $this->errorIfOpened();
365
        $this->snapshot = true;
366
367
        return $this;
368
    }
369
370
    /**
371
     * Sorts the results by given fields
372
     * @link http://www.php.net/manual/en/mongocursor.sort.php
373
     * @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
374
     * @throws MongoCursorException
375
     * @return MongoCursor Returns the same cursor that this method was called on
376
     */
377
    public function sort(array $fields)
378
    {
379
        $this->errorIfOpened();
380
        $this->sort = $fields;
381
382
        return $this;
383
    }
384
385
    /**
386
     * Sets whether this cursor will be left open after fetching the last results
387
     * @link http://www.php.net/manual/en/mongocursor.tailable.php
388
     * @param bool $tail If the cursor should be tailable.
389
     * @return MongoCursor Returns this cursor
390
     */
391
    public function tailable($tail = true)
392
    {
393
        $this->errorIfOpened();
394
        $this->tailable = $tail;
395
396
        return $this;
397
    }
398
399
    /**
400
     * @return int|null
401
     */
402
    protected function convertCursorType()
403
    {
404
        if (! $this->tailable) {
405
            return null;
406
        }
407
408
        return $this->awaitData ? Find::TAILABLE_AWAIT : Find::TAILABLE;
409
    }
410
411
    /**
412
     * @return array
413
     */
414
    protected function convertModifiers()
415
    {
416
        $modifiers = array_key_exists('modifiers', $this->options) ? $this->options['modifiers'] : [];
417
418
        foreach (['hint', 'snapshot'] as $modifier) {
419
            if ($this->$modifier === null) {
420
                continue;
421
            }
422
423
            $modifiers['$' . $modifier] = $this->$modifier;
424
        }
425
426
        return $modifiers;
427
    }
428
429
    /**
430
     * @return array
431
     */
432
    protected function convertProjection()
433
    {
434
        return TypeConverter::fromLegacy($this->projection);
435
    }
436
437
    /**
438
     * @return Cursor
439
     */
440
    protected function ensureCursor()
441
    {
442
        if ($this->cursor === null) {
443
            $this->doQuery();
444
        }
445
446
        return $this->cursor;
447
    }
448
449
    /**
450
     * @param \Traversable $traversable
451
     * @return \Generator
452
     */
453
    protected function wrapTraversable(\Traversable $traversable)
454
    {
455
        foreach ($traversable as $key => $value) {
456
            if (isset($value->_id) && ($value->_id instanceof \MongoDB\BSON\ObjectID || !is_object($value->_id))) {
457
                $key = (string) $value->_id;
458
            }
459
            yield $key => $value;
460
        }
461
    }
462
463
    /**
464
     * @return array
465
     */
466
    protected function getCursorInfo()
467
    {
468
        return [
469
            'ns' => $this->ns,
470
            'limit' => $this->limit,
471
            'batchSize' => $this->batchSize,
472
            'skip' => $this->skip,
473
            'flags' => $this->flags,
474
            'query' => $this->query,
475
            'fields' => $this->projection,
476
        ];
477
    }
478
479
    /**
480
     * @return array
481
     */
482
    public function __sleep()
483
    {
484
        return [
485
            'allowPartialResults',
486
            'awaitData',
487
            'flags',
488
            'hint',
489
            'limit',
490
            'maxTimeMS',
491
            'noCursorTimeout',
492
            'optionNames',
493
            'options',
494
            'projection',
495
            'query',
496
            'skip',
497
            'snapshot',
498
            'sort',
499
            'tailable',
500
        ] + parent::__sleep();
501
    }
502
}
503