Completed
Push — master ( 53f522...c56abb )
by Andreas
08:48
created

MongoCursor::wrapTraversable()   B

Complexity

Conditions 5
Paths 3

Size

Total Lines 9
Code Lines 5

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 9
rs 8.8571
cc 5
eloc 5
nc 3
nop 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
use Alcaeus\MongoDbAdapter\AbstractCursor;
17
use Alcaeus\MongoDbAdapter\TypeConverter;
18
use Alcaeus\MongoDbAdapter\ExceptionConverter;
19
use MongoDB\Driver\Cursor;
20
use MongoDB\Driver\ReadPreference;
21
use MongoDB\Operation\Find;
22
23
/**
24
 * Result object for database query.
25
 * @link http://www.php.net/manual/en/class.mongocursor.php
26
 */
27
class MongoCursor extends AbstractCursor implements Iterator
1 ignored issue
show
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
28
{
29
    /**
30
     * @var bool
31
     */
32
    public static $slaveOkay = false;
33
34
    /**
35
     * @var int
36
     */
37
    public static $timeout = 30000;
38
39
    /**
40
     * @var array
41
     */
42
    protected $optionNames = [
43
        'allowPartialResults',
44
        'batchSize',
45
        'cursorType',
46
        'limit',
47
        'maxTimeMS',
48
        'modifiers',
49
        'noCursorTimeout',
50
        'projection',
51
        'readPreference',
52
        'skip',
53
        'sort',
54
    ];
55
56
    /**
57
     * @var array
58
     */
59
    protected $projection;
60
61
    /**
62
     * @var array
63
     */
64
    protected $query;
65
66
    protected $allowPartialResults;
67
    protected $awaitData;
68
    protected $flags = 0;
69
    protected $hint;
70
    protected $limit;
71
    protected $maxTimeMS;
72
    protected $noCursorTimeout;
73
    protected $options = [];
74
    protected $skip;
75
    protected $snapshot;
76
    protected $sort;
77
    protected $tailable;
78
79
    /**
80
     * Create a new cursor
81
     * @link http://www.php.net/manual/en/mongocursor.construct.php
82
     * @param MongoClient $connection Database connection.
83
     * @param string $ns Full name of database and collection.
84
     * @param array $query Database query.
85
     * @param array $fields Fields to return.
86
     */
87
    public function __construct(MongoClient $connection, $ns, array $query = array(), array $fields = array())
88
    {
89
        parent::__construct($connection, $ns);
90
91
        $this->query = $query;
92
        $this->projection = $fields;
93
    }
94
95
    /**
96
     * Adds a top-level key/value pair to a query
97
     * @link http://www.php.net/manual/en/mongocursor.addoption.php
98
     * @param string $key Fieldname to add.
99
     * @param mixed $value Value to add.
100
     * @throws MongoCursorException
101
     * @return MongoCursor Returns this cursor
102
     */
103
    public function addOption($key, $value)
104
    {
105
        $this->errorIfOpened();
106
        $this->options[$key] = $value;
107
108
        return $this;
109
    }
110
111
    /**
112
     * (PECL mongo &gt;= 1.2.11)<br/>
113
     * Sets whether this cursor will wait for a while for a tailable cursor to return more data
114
     * @param bool $wait [optional] <p>If the cursor should wait for more data to become available.</p>
115
     * @return MongoCursor Returns this cursor.
116
     */
117
    public function awaitData($wait = true)
118
    {
119
        $this->errorIfOpened();
120
        $this->awaitData = $wait;
121
122
        return $this;
123
    }
124
125
126
    /**
127
     * Counts the number of results for this query
128
     * @link http://www.php.net/manual/en/mongocursor.count.php
129
     * @param bool $foundOnly Send cursor limit and skip information to the count function, if applicable.
130
     * @return int The number of documents returned by this cursor's query.
131
     */
132
    public function count($foundOnly = false)
133
    {
134
        if ($foundOnly && $this->cursor !== null) {
135
            return iterator_count($this->ensureIterator());
136
        }
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) {
1 ignored issue
show
Bug introduced by
The class MongoDB\Driver\Exception\ExecutionTimeoutException does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
147
            throw new MongoCursorTimeoutException($e->getMessage(), $e->getCode(), $e);
148
        } catch (\MongoDB\Driver\Exception\Exception $e) {
1 ignored issue
show
Bug introduced by
The class MongoDB\Driver\Exception\Exception does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
149
            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);
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->collection->find(...this->query), $options) of type object<MongoDB\Operation\Cursor> is incompatible with the declared type object<MongoDB\Driver\Cursor> of property $cursor.

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...
167
        } catch (\MongoDB\Driver\Exception\ExecutionTimeoutException $e) {
1 ignored issue
show
Bug introduced by
The class MongoDB\Driver\Exception\ExecutionTimeoutException does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
168
            throw new MongoCursorTimeoutException($e->getMessage(), $e->getCode(), $e);
169
        } catch (\MongoDB\Driver\Exception\Exception $e) {
1 ignored issue
show
Bug introduced by
The class MongoDB\Driver\Exception\Exception does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
170
            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
        $this->errorIfOpened();
221
        $this->notImplemented();
222
    }
223
224
    /**
225
     * Gives the database a hint about the query
226
     * @link http://www.php.net/manual/en/mongocursor.hint.php
227
     * @param array|string $keyPattern Indexes to use for the query.
228
     * @throws MongoCursorException
229
     * @return MongoCursor Returns this cursor
230
     */
231
    public function hint($keyPattern)
232
    {
233
        $this->errorIfOpened();
234
        $this->hint = $keyPattern;
235
236
        return $this;
237
    }
238
239
    /**
240
     * Sets whether this cursor will timeout
241
     * @link http://www.php.net/manual/en/mongocursor.immortal.php
242
     * @param bool $liveForever If the cursor should be immortal.
243
     * @throws MongoCursorException
244
     * @return MongoCursor Returns this cursor
245
     */
246
    public function immortal($liveForever = true)
247
    {
248
        $this->errorIfOpened();
249
        $this->noCursorTimeout = $liveForever;
250
251
        return $this;
252
    }
253
254
    /**
255
     * Limits the number of results returned
256
     * @link http://www.php.net/manual/en/mongocursor.limit.php
257
     * @param int $num The number of results to return.
258
     * @throws MongoCursorException
259
     * @return MongoCursor Returns this cursor
260
     */
261
    public function limit($num)
262
    {
263
        $this->errorIfOpened();
264
        $this->limit = $num;
265
266
        return $this;
267
    }
268
269
    /**
270
     * @param int $ms
271
     * @return $this
272
     * @throws MongoCursorException
273
     */
274
    public function maxTimeMS($ms)
275
    {
276
        $this->errorIfOpened();
277
        $this->maxTimeMS = $ms;
278
279
        return $this;
280
    }
281
282
    /**
283
     * @link http://www.php.net/manual/en/mongocursor.partial.php
284
     * @param bool $okay [optional] <p>If receiving partial results is okay.</p>
285
     * @return MongoCursor Returns this cursor.
286
     */
287
    public function partial($okay = true)
288
    {
289
        $this->allowPartialResults = $okay;
290
291
        return $this;
292
    }
293
294
    /**
295
     * Clears the cursor
296
     * @link http://www.php.net/manual/en/mongocursor.reset.php
297
     * @return void
298
     */
299
    public function reset()
300
    {
301
        parent::reset();
302
    }
303
304
    /**
305
     * @link http://www.php.net/manual/en/mongocursor.setflag.php
306
     * @param int $flag
307
     * @param bool $set
308
     * @return MongoCursor
309
     */
310
    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...
311
    {
312
        $this->notImplemented();
313
    }
314
315
    /**
316
     * Skips a number of results
317
     * @link http://www.php.net/manual/en/mongocursor.skip.php
318
     * @param int $num The number of results to skip.
319
     * @throws MongoCursorException
320
     * @return MongoCursor Returns this cursor
321
     */
322
    public function skip($num)
323
    {
324
        $this->errorIfOpened();
325
        $this->skip = $num;
326
327
        return $this;
328
    }
329
330
    /**
331
     * Sets whether this query can be done on a slave
332
     * This method will override the static class variable slaveOkay.
333
     * @link http://www.php.net/manual/en/mongocursor.slaveOkay.php
334
     * @param boolean $okay If it is okay to query the slave.
335
     * @throws MongoCursorException
336
     * @return MongoCursor Returns this cursor
337
     */
338
    public function slaveOkay($okay = true)
339
    {
340
        $this->errorIfOpened();
341
342
        $this->setReadPreferenceFromSlaveOkay($okay);
343
344
        return $this;
345
    }
346
347
    /**
348
     * Use snapshot mode for the query
349
     * @link http://www.php.net/manual/en/mongocursor.snapshot.php
350
     * @throws MongoCursorException
351
     * @return MongoCursor Returns this cursor
352
     */
353
    public function snapshot()
354
    {
355
        $this->errorIfOpened();
356
        $this->snapshot = true;
357
358
        return $this;
359
    }
360
361
    /**
362
     * Sorts the results by given fields
363
     * @link http://www.php.net/manual/en/mongocursor.sort.php
364
     * @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
365
     * @throws MongoCursorException
366
     * @return MongoCursor Returns the same cursor that this method was called on
367
     */
368
    public function sort(array $fields)
369
    {
370
        $this->errorIfOpened();
371
        $this->sort = $fields;
372
373
        return $this;
374
    }
375
376
    /**
377
     * Sets whether this cursor will be left open after fetching the last results
378
     * @link http://www.php.net/manual/en/mongocursor.tailable.php
379
     * @param bool $tail If the cursor should be tailable.
380
     * @return MongoCursor Returns this cursor
381
     */
382
    public function tailable($tail = true)
383
    {
384
        $this->errorIfOpened();
385
        $this->tailable = $tail;
386
387
        return $this;
388
    }
389
390
    /**
391
     * @return int|null
392
     */
393
    protected function convertCursorType()
394
    {
395
        if (! $this->tailable) {
396
            return null;
397
        }
398
399
        return $this->awaitData ? Find::TAILABLE_AWAIT : Find::TAILABLE;
400
    }
401
402
    protected function convertModifiers()
403
    {
404
        $modifiers = array_key_exists('modifiers', $this->options) ? $this->options['modifiers'] : [];
405
406
        foreach (['hint', 'snapshot'] as $modifier) {
407
            if ($this->$modifier === null) {
408
                continue;
409
            }
410
411
            $modifiers['$' . $modifier] = $this->$modifier;
412
        }
413
414
        return $modifiers;
415
    }
416
417
    /**
418
     * @return Cursor
419
     */
420
    protected function ensureCursor()
421
    {
422
        if ($this->cursor === null) {
423
            $this->doQuery();
424
        }
425
426
        return $this->cursor;
427
    }
428
429
    /**
430
     * @param \Traversable $traversable
431
     * @return \Generator
432
     */
433
    protected function wrapTraversable(\Traversable $traversable)
434
    {
435
        foreach ($traversable as $key => $value) {
436
            if (isset($value->_id) && ($value->_id instanceof \MongoDB\BSON\ObjectID || !is_object($value->_id))) {
1 ignored issue
show
Bug introduced by
The class MongoDB\BSON\ObjectID does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
437
                $key = (string) $value->_id;
438
            }
439
            yield $key => $value;
440
        }
441
    }
442
443
    /**
444
     * @return array
445
     */
446
    protected function getCursorInfo()
447
    {
448
        return [
449
            'ns' => $this->ns,
450
            'limit' => $this->limit,
451
            'batchSize' => $this->batchSize,
452
            'skip' => $this->skip,
453
            'flags' => $this->flags,
454
            'query' => $this->query,
455
            'fields' => $this->projection,
456
        ];
457
    }
458
}
459