Completed
Pull Request — master (#28)
by Andreas
03:41
created

MongoCollection::getIndexInfo()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 13
Code Lines 8

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 13
rs 9.4285
cc 1
eloc 8
nc 1
nop 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
use Alcaeus\MongoDbAdapter\Helper;
17
use Alcaeus\MongoDbAdapter\TypeConverter;
18
use Alcaeus\MongoDbAdapter\ExceptionConverter;
19
20
/**
21
 * Represents a database collection.
22
 * @link http://www.php.net/manual/en/class.mongocollection.php
23
 */
24
class MongoCollection
25
{
26
    use Helper\ReadPreference;
27
    use Helper\SlaveOkay;
28
    use Helper\WriteConcern;
29
30
    const ASCENDING = 1;
31
    const DESCENDING = -1;
32
33
    /**
34
     * @var MongoDB
35
     */
36
    public $db = NULL;
37
38
    /**
39
     * @var string
40
     */
41
    protected $name;
42
43
    /**
44
     * @var \MongoDB\Collection
45
     */
46
    protected $collection;
47
48
    /**
49
     * Creates a new collection
50
     *
51
     * @link http://www.php.net/manual/en/mongocollection.construct.php
52
     * @param MongoDB $db Parent database.
53
     * @param string $name Name for this collection.
54
     * @throws Exception
55
     * @return MongoCollection
0 ignored issues
show
Comprehensibility Best Practice introduced by
Adding a @return annotation to constructors is generally not recommended as a constructor does not have a meaningful return value.

Adding a @return annotation to a constructor is not recommended, since a constructor does not have a meaningful return value.

Please refer to the PHP core documentation on constructors.

Loading history...
56
     */
57
    public function __construct(MongoDB $db, $name)
58
    {
59
        $this->checkCollectionName($name);
60
        $this->db = $db;
61
        $this->name = $name;
62
63
        $this->setReadPreferenceFromArray($db->getReadPreference());
64
        $this->setWriteConcernFromArray($db->getWriteConcern());
65
66
        $this->createCollectionObject();
67
    }
68
69
    /**
70
     * Gets the underlying collection for this object
71
     *
72
     * @internal This part is not of the ext-mongo API and should not be used
73
     * @return \MongoDB\Collection
74
     */
75
    public function getCollection()
76
    {
77
        return $this->collection;
78
    }
79
80
    /**
81
     * String representation of this collection
82
     *
83
     * @link http://www.php.net/manual/en/mongocollection.--tostring.php
84
     * @return string Returns the full name of this collection.
85
     */
86
    public function __toString()
87
    {
88
        return (string) $this->db . '.' . $this->name;
89
    }
90
91
    /**
92
     * Gets a collection
93
     *
94
     * @link http://www.php.net/manual/en/mongocollection.get.php
95
     * @param string $name The next string in the collection name.
96
     * @return MongoCollection
97
     */
98
    public function __get($name)
99
    {
100
        // Handle w and wtimeout properties that replicate data stored in $readPreference
101
        if ($name === 'w' || $name === 'wtimeout') {
102
            return $this->getWriteConcern()[$name];
103
        }
104
105
        return $this->db->selectCollection($this->name . '.' . $name);
106
    }
107
108
    /**
109
     * @param string $name
110
     * @param mixed $value
111
     */
112
    public function __set($name, $value)
113
    {
114
        if ($name === 'w' || $name === 'wtimeout') {
115
            $this->setWriteConcernFromArray([$name => $value] + $this->getWriteConcern());
116
            $this->createCollectionObject();
117
        }
118
    }
119
120
    /**
121
     * Perform an aggregation using the aggregation framework
122
     *
123
     * @link http://www.php.net/manual/en/mongocollection.aggregate.php
124
     * @param array $pipeline
125
     * @param array $op
126
     * @return array
127
     */
128
    public function aggregate(array $pipeline, array $op = [])
129
    {
130
        if (! TypeConverter::isNumericArray($pipeline)) {
131
            $pipeline = [];
132
            $options = [];
133
134
            $i = 0;
135
            foreach (func_get_args() as $operator) {
136
                $i++;
137
                if (! is_array($operator)) {
138
                    trigger_error("Argument $i is not an array", E_WARNING);
139
                    return;
140
                }
141
142
                $pipeline[] = $operator;
143
            }
144
        } else {
145
            $options = $op;
146
        }
147
148
        $command = [
149
            'aggregate' => $this->name,
150
            'pipeline' => $pipeline
151
        ];
152
153
        $command += $options;
154
155
        try {
156
            return $this->db->command($command);
157
        } catch (MongoCursorTimeoutException $e) {
158
            throw new MongoExecutionTimeoutException($e->getMessage(), $e->getCode(), $e);
159
        }
160
161
    }
162
163
    /**
164
     * Execute an aggregation pipeline command and retrieve results through a cursor
165
     *
166
     * @link http://php.net/manual/en/mongocollection.aggregatecursor.php
167
     * @param array $pipeline
168
     * @param array $options
169
     * @return MongoCommandCursor
170
     */
171
    public function aggregateCursor(array $pipeline, array $options = [])
172
    {
173
        // Build command manually, can't use mongo-php-library here
174
        $command = [
175
            'aggregate' => $this->name,
176
            'pipeline' => $pipeline
177
        ];
178
179
        // Convert cursor option
180
        if (! isset($options['cursor'])) {
181
            $options['cursor'] = true;
182
        }
183
184
        $command += $options;
185
186
        $cursor = new MongoCommandCursor($this->db->getConnection(), (string) $this, $command);
187
        $cursor->setReadPreference($this->getReadPreference());
0 ignored issues
show
Documentation introduced by
$this->getReadPreference() is of type array, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
188
189
        return $cursor;
190
    }
191
192
    /**
193
     * Returns this collection's name
194
     *
195
     * @link http://www.php.net/manual/en/mongocollection.getname.php
196
     * @return string
197
     */
198
    public function getName()
199
    {
200
        return $this->name;
201
    }
202
203
    /**
204
     * {@inheritdoc}
205
     */
206
    public function setReadPreference($readPreference, $tags = null)
207
    {
208
        $result = $this->setReadPreferenceFromParameters($readPreference, $tags);
209
        $this->createCollectionObject();
210
211
        return $result;
212
    }
213
214
    /**
215
     * {@inheritdoc}
216
     */
217
    public function setWriteConcern($wstring, $wtimeout = 0)
218
    {
219
        $result = $this->setWriteConcernFromParameters($wstring, $wtimeout);
220
        $this->createCollectionObject();
221
222
        return $result;
223
    }
224
225
    /**
226
     * Drops this collection
227
     *
228
     * @link http://www.php.net/manual/en/mongocollection.drop.php
229
     * @return array Returns the database response.
230
     */
231
    public function drop()
232
    {
233
        return TypeConverter::toLegacy($this->collection->drop());
234
    }
235
236
    /**
237
     * Validates this collection
238
     *
239
     * @link http://www.php.net/manual/en/mongocollection.validate.php
240
     * @param bool $scan_data Only validate indices, not the base collection.
241
     * @return array Returns the database's evaluation of this object.
242
     */
243
    public function validate($scan_data = FALSE)
244
    {
245
        $command = [
246
            'validate' => $this->name,
247
            'full'     => $scan_data,
248
        ];
249
250
        return $this->db->command($command);
251
    }
252
253
    /**
254
     * Inserts an array into the collection
255
     *
256
     * @link http://www.php.net/manual/en/mongocollection.insert.php
257
     * @param array|object $a
258
     * @param array $options
259
     * @throws MongoException if the inserted document is empty or if it contains zero-length keys. Attempting to insert an object with protected and private properties will cause a zero-length key error.
260
     * @throws MongoCursorException if the "w" option is set and the write fails.
261
     * @throws MongoCursorTimeoutException if the "w" option is set to a value greater than one and the operation takes longer than MongoCursor::$timeout milliseconds to complete. This does not kill the operation on the server, it is a client-side timeout. The operation in MongoCollection::$wtimeout is milliseconds.
262
     * @return bool|array Returns an array containing the status of the insertion if the "w" option is set.
263
     */
264
    public function insert(&$a, array $options = [])
265
    {
266
        if (! $this->ensureDocumentHasMongoId($a)) {
267
            trigger_error(sprintf('%s(): expects parameter %d to be an array or object, %s given', __METHOD__, 1, gettype($a)), E_USER_WARNING);
268
            return;
269
        }
270
271
        if (! count((array)$a)) {
272
            throw new \MongoException('document must be an array or object');
273
        }
274
275
        try {
276
            $result = $this->collection->insertOne(
277
                TypeConverter::fromLegacy($a),
278
                $this->convertWriteConcernOptions($options)
279
            );
280
        } 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...
281
            throw ExceptionConverter::toLegacy($e);
282
        }
283
284
        if (! $result->isAcknowledged()) {
285
            return true;
286
        }
287
288
        return [
289
            'ok' => 1.0,
290
            'n' => 0,
291
            'err' => null,
292
            'errmsg' => null,
293
        ];
294
    }
295
296
    /**
297
     * Inserts multiple documents into this collection
298
     *
299
     * @link http://www.php.net/manual/en/mongocollection.batchinsert.php
300
     * @param array $a An array of arrays.
301
     * @param array $options Options for the inserts.
302
     * @throws MongoCursorException
303
     * @return mixed If "safe" is set, returns an associative array with the status of the inserts ("ok") and any error that may have occured ("err"). Otherwise, returns TRUE if the batch insert was successfully sent, FALSE otherwise.
304
     */
305
    public function batchInsert(array &$a, array $options = [])
306
    {
307
        if (empty($a)) {
308
            throw new \MongoException('No write ops were included in the batch');
309
        }
310
311
        $continueOnError = isset($options['continueOnError']) && $options['continueOnError'];
312
313
        foreach ($a as $key => $item) {
314
            try {
315
                if (! $this->ensureDocumentHasMongoId($a[$key])) {
316
                    if ($continueOnError) {
317
                        unset($a[$key]);
318
                    } else {
319
                        trigger_error(sprintf('%s expects parameter %d to be an array or object, %s given', __METHOD__, 1, gettype($a)), E_USER_WARNING);
320
                        return;
321
                    }
322
                }
323
            } catch (MongoException $e) {
324
                if ( ! $continueOnError) {
325
                    throw $e;
326
                }
327
            }
328
        }
329
330
        try {
331
            $result = $this->collection->insertMany(
332
                TypeConverter::fromLegacy(array_values($a)),
333
                $this->convertWriteConcernOptions($options)
334
            );
335
        } 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...
336
            throw ExceptionConverter::toLegacy($e);
337
        }
338
339
        if (! $result->isAcknowledged()) {
340
            return true;
341
        }
342
343
        return [
344
            'connectionId' => 0,
345
            'n' => 0,
346
            'syncMillis' => 0,
347
            'writtenTo' => null,
348
            'err' => null,
349
        ];
350
    }
351
352
    /**
353
     * Update records based on a given criteria
354
     *
355
     * @link http://www.php.net/manual/en/mongocollection.update.php
356
     * @param array $criteria Description of the objects to update.
357
     * @param array $newobj The object with which to update the matching records.
358
     * @param array $options
359
     * @throws MongoCursorException
360
     * @return boolean
361
     */
362
    public function update(array $criteria , array $newobj, array $options = [])
363
    {
364
        $multiple = isset($options['multiple']) ? $options['multiple'] : false;
365
        $method = $multiple ? 'updateMany' : 'updateOne';
366
        unset($options['multiple']);
367
368
        try {
369
            /** @var \MongoDB\UpdateResult $result */
370
            $result = $this->collection->$method(
371
                TypeConverter::fromLegacy($criteria),
372
                TypeConverter::fromLegacy($newobj),
373
                $this->convertWriteConcernOptions($options)
374
            );
375
        } 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...
376
            throw ExceptionConverter::toLegacy($e);
377
        }
378
379
        if (! $result->isAcknowledged()) {
380
            return true;
381
        }
382
383
        return [
384
            'ok' => 1.0,
385
            'nModified' => $result->getModifiedCount(),
386
            'n' => $result->getMatchedCount(),
387
            'err' => null,
388
            'errmsg' => null,
389
            'updatedExisting' => $result->getUpsertedCount() == 0,
390
        ];
391
    }
392
393
    /**
394
     * Remove records from this collection
395
     *
396
     * @link http://www.php.net/manual/en/mongocollection.remove.php
397
     * @param array $criteria Query criteria for the documents to delete.
398
     * @param array $options An array of options for the remove operation.
399
     * @throws MongoCursorException
400
     * @throws MongoCursorTimeoutException
401
     * @return bool|array Returns an array containing the status of the removal
402
     * if the "w" option is set. Otherwise, returns TRUE.
403
     */
404
    public function remove(array $criteria = [], array $options = [])
405
    {
406
        $multiple = isset($options['justOne']) ? !$options['justOne'] : true;
407
        $method = $multiple ? 'deleteMany' : 'deleteOne';
408
409
        try {
410
            /** @var \MongoDB\DeleteResult $result */
411
            $result = $this->collection->$method(
412
                TypeConverter::fromLegacy($criteria),
413
                $this->convertWriteConcernOptions($options)
414
            );
415
        } 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...
416
            throw ExceptionConverter::toLegacy($e);
417
        }
418
419
        if (! $result->isAcknowledged()) {
420
            return true;
421
        }
422
423
        return [
424
            'ok' => 1.0,
425
            'n' => $result->getDeletedCount(),
426
            'err' => null,
427
            'errmsg' => null
428
        ];
429
    }
430
431
    /**
432
     * Querys this collection
433
     *
434
     * @link http://www.php.net/manual/en/mongocollection.find.php
435
     * @param array $query The fields for which to search.
436
     * @param array $fields Fields of the results to return.
437
     * @return MongoCursor
438
     */
439 View Code Duplication
    public function find(array $query = [], array $fields = [])
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
440
    {
441
        $cursor = new MongoCursor($this->db->getConnection(), (string) $this, $query, $fields);
442
        $cursor->setReadPreference($this->getReadPreference());
0 ignored issues
show
Documentation introduced by
$this->getReadPreference() is of type array, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
443
444
        return $cursor;
445
    }
446
447
    /**
448
     * Retrieve a list of distinct values for the given key across a collection
449
     *
450
     * @link http://www.php.net/manual/ru/mongocollection.distinct.php
451
     * @param string $key The key to use.
452
     * @param array $query An optional query parameters
453
     * @return array|bool Returns an array of distinct values, or FALSE on failure
454
     */
455
    public function distinct($key, array $query = [])
456
    {
457
        try {
458
            return array_map([TypeConverter::class, 'toLegacy'], $this->collection->distinct($key, $query));
459
        } 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...
460
            return false;
461
        }
462
    }
463
464
    /**
465
     * Update a document and return it
466
     *
467
     * @link http://www.php.net/manual/ru/mongocollection.findandmodify.php
468
     * @param array $query The query criteria to search for.
469
     * @param array $update The update criteria.
470
     * @param array $fields Optionally only return these fields.
471
     * @param array $options An array of options to apply, such as remove the match document from the DB and return it.
472
     * @return array Returns the original document, or the modified document when new is set.
473
     */
474
    public function findAndModify(array $query, array $update = null, array $fields = null, array $options = [])
475
    {
476
        $query = TypeConverter::fromLegacy($query);
477
        try {
478
            if (isset($options['remove'])) {
479
                unset($options['remove']);
480
                $document = $this->collection->findOneAndDelete($query, $options);
481
            } else {
482
                $update = is_array($update) ? TypeConverter::fromLegacy($update) : [];
483
484
                if (isset($options['new'])) {
485
                    $options['returnDocument'] = \MongoDB\Operation\FindOneAndUpdate::RETURN_DOCUMENT_AFTER;
486
                    unset($options['new']);
487
                }
488
489
                $options['projection'] = is_array($fields) ? TypeConverter::fromLegacy($fields) : [];
490
491
                $document = $this->collection->findOneAndUpdate($query, $update, $options);
492
            }
493
        } catch (\MongoDB\Driver\Exception\ConnectionException $e) {
1 ignored issue
show
Bug introduced by
The class MongoDB\Driver\Exception\ConnectionException 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...
494
            throw new MongoResultException($e->getMessage(), $e->getCode(), $e);
495
        } 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...
496
            throw ExceptionConverter::toLegacy($e, 'MongoResultException');
497
        }
498
499
        if ($document) {
500
            $document = TypeConverter::toLegacy($document);
501
        }
502
503
        return $document;
504
    }
505
506
    /**
507
     * Querys this collection, returning a single element
508
     *
509
     * @link http://www.php.net/manual/en/mongocollection.findone.php
510
     * @param array $query The fields for which to search.
511
     * @param array $fields Fields of the results to return.
512
     * @param array $options
513
     * @return array|null
514
     */
515
    public function findOne(array $query = [], array $fields = [], array $options = [])
516
    {
517
        $options = ['projection' => $fields] + $options;
518
        try {
519
            $document = $this->collection->findOne(TypeConverter::fromLegacy($query), $options);
520
        } 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...
521
            throw ExceptionConverter::toLegacy($e);
522
        }
523
524
        if ($document !== null) {
525
            $document = TypeConverter::toLegacy($document);
526
        }
527
528
        return $document;
529
    }
530
531
    /**
532
     * Creates an index on the given field(s), or does nothing if the index already exists
533
     *
534
     * @link http://www.php.net/manual/en/mongocollection.createindex.php
535
     * @param array $keys Field or fields to use as index.
536
     * @param array $options [optional] This parameter is an associative array of the form array("optionname" => <boolean>, ...).
537
     * @return array Returns the database response.
538
     *
539
     * @todo This method does not yet return the correct result
540
     */
541
    public function createIndex($keys, array $options = [])
542
    {
543
        if (is_string($keys)) {
544
            if (empty($keys)) {
545
                throw new MongoException('empty string passed as key field');
546
            }
547
            $keys = [$keys => 1];
548
        }
549
550
        if (is_object($keys)) {
551
            $keys = (array) $keys;
552
        }
553
554
        if (! is_array($keys) || ! count($keys)) {
555
            throw new MongoException('keys cannot be empty');
556
        }
557
558
        // duplicate
559
        $neededOptions = ['unique' => 1, 'sparse' => 1, 'expireAfterSeconds' => 1, 'background' => 1, 'dropDups' => 1];
560
        $indexOptions = array_intersect_key($options, $neededOptions);
561
        $indexes = $this->collection->listIndexes();
562
        foreach ($indexes as $index) {
563
564
            if (! empty($options['name']) && $index->getName() === $options['name']) {
565
                throw new \MongoResultException(sprintf('index with name: %s already exists', $index->getName()));
566
            }
567
568
            if ($index->getKey() == $keys) {
569
                $currentIndexOptions = array_intersect_key($index->__debugInfo(), $neededOptions);
570
571
                unset($currentIndexOptions['name']);
572
                if ($currentIndexOptions != $indexOptions) {
573
                    throw new \MongoResultException('Index with same keys but different options already exists');
574
                }
575
576
                return [
577
                    'createdCollectionAutomatically' => false,
578
                    'numIndexesBefore' => count($indexes),
579
                    'numIndexesAfter' => count($indexes),
580
                    'note' => 'all indexes already exist',
581
                    'ok' => 1.0
582
                ];
583
            }
584
        }
585
586
        try {
587
            $this->collection->createIndex($keys, $this->convertWriteConcernOptions($options));
588
        } 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...
589
            throw ExceptionConverter::toLegacy($e);
590
        }
591
592
        return [
593
            'createdCollectionAutomatically' => true,
594
            'numIndexesBefore' => count($indexes),
595
            'numIndexesAfter' => count($indexes) + 1,
596
            'ok' => 1.0
597
        ];
598
    }
599
600
    /**
601
     * Creates an index on the given field(s), or does nothing if the index already exists
602
     *
603
     * @link http://www.php.net/manual/en/mongocollection.ensureindex.php
604
     * @param array $keys Field or fields to use as index.
605
     * @param array $options [optional] This parameter is an associative array of the form array("optionname" => <boolean>, ...).
606
     * @return boolean always true
607
     * @deprecated Use MongoCollection::createIndex() instead.
608
     */
609
    public function ensureIndex(array $keys, array $options = [])
610
    {
611
        $this->createIndex($keys, $options);
612
613
        return true;
614
    }
615
616
    /**
617
     * Deletes an index from this collection
618
     *
619
     * @link http://www.php.net/manual/en/mongocollection.deleteindex.php
620
     * @param string|array $keys Field or fields from which to delete the index.
621
     * @return array Returns the database response.
622
     */
623
    public function deleteIndex($keys)
624
    {
625
        if (is_string($keys)) {
626
            $indexName = $keys;
627
        } elseif (is_array($keys)) {
628
            $indexName = \MongoDB\generate_index_name($keys);
629
        } else {
630
            throw new \InvalidArgumentException();
631
        }
632
633
        return TypeConverter::toLegacy($this->collection->dropIndex($indexName));
634
    }
635
636
    /**
637
     * Delete all indexes for this collection
638
     *
639
     * @link http://www.php.net/manual/en/mongocollection.deleteindexes.php
640
     * @return array Returns the database response.
641
     */
642
    public function deleteIndexes()
643
    {
644
        return TypeConverter::toLegacy($this->collection->dropIndexes());
645
    }
646
647
    /**
648
     * Returns an array of index names for this collection
649
     *
650
     * @link http://www.php.net/manual/en/mongocollection.getindexinfo.php
651
     * @return array Returns a list of index names.
652
     */
653
    public function getIndexInfo()
654
    {
655
        $convertIndex = function(\MongoDB\Model\IndexInfo $indexInfo) {
656
            return [
657
                'v' => $indexInfo->getVersion(),
658
                'key' => $indexInfo->getKey(),
659
                'name' => $indexInfo->getName(),
660
                'ns' => $indexInfo->getNamespace(),
661
            ];
662
        };
663
664
        return array_map($convertIndex, iterator_to_array($this->collection->listIndexes()));
665
    }
666
667
    /**
668
     * Counts the number of documents in this collection
669
     *
670
     * @link http://www.php.net/manual/en/mongocollection.count.php
671
     * @param array|stdClass $query
672
     * @param array $options
673
     * @return int Returns the number of documents matching the query.
674
     */
675
    public function count($query = [], array $options = [])
676
    {
677
        try {
678
            return $this->collection->count(TypeConverter::fromLegacy($query), $options);
679
        } 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...
680
            throw ExceptionConverter::toLegacy($e);
681
        }
682
    }
683
684
    /**
685
     * Saves an object to this collection
686
     *
687
     * @link http://www.php.net/manual/en/mongocollection.save.php
688
     * @param array|object $a Array to save. If an object is used, it may not have protected or private properties.
689
     * @param array $options Options for the save.
690
     * @throws MongoException if the inserted document is empty or if it contains zero-length keys. Attempting to insert an object with protected and private properties will cause a zero-length key error.
691
     * @throws MongoCursorException if the "w" option is set and the write fails.
692
     * @throws MongoCursorTimeoutException if the "w" option is set to a value greater than one and the operation takes longer than MongoCursor::$timeout milliseconds to complete. This does not kill the operation on the server, it is a client-side timeout. The operation in MongoCollection::$wtimeout is milliseconds.
693
     * @return array|boolean If w was set, returns an array containing the status of the save.
694
     * Otherwise, returns a boolean representing if the array was not empty (an empty array will not be inserted).
695
     */
696
    public function save(&$a, array $options = [])
697
    {
698
        $id = $this->ensureDocumentHasMongoId($a);
699
700
        $document = (array) $a;
701
702
        $options['upsert'] = true;
703
704
        try {
705
            /** @var \MongoDB\UpdateResult $result */
706
            $result = $this->collection->replaceOne(
707
                TypeConverter::fromLegacy(['_id' => $id]),
708
                TypeConverter::fromLegacy($document),
709
                $this->convertWriteConcernOptions($options)
710
            );
711
        } 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...
712
            ExceptionConverter::toLegacy($e);
713
        }
714
715
        if (!$result->isAcknowledged()) {
716
            return true;
717
        }
718
719
        return [
720
            'ok' => 1.0,
721
            'nModified' => $result->getModifiedCount(),
722
            'n' => $result->getMatchedCount(),
723
            'err' => null,
724
            'errmsg' => null,
725
            'updatedExisting' => $result->getUpsertedCount() == 0,
726
        ];
727
    }
728
729
    /**
730
     * Creates a database reference
731
     *
732
     * @link http://www.php.net/manual/en/mongocollection.createdbref.php
733
     * @param array|object $document_or_id Object to which to create a reference.
734
     * @return array Returns a database reference array.
735
     */
736 View Code Duplication
    public function createDBRef($document_or_id)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
737
    {
738
        if ($document_or_id instanceof \MongoId) {
739
            $id = $document_or_id;
740
        } elseif (is_object($document_or_id)) {
741
            if (! isset($document_or_id->_id)) {
742
                return null;
743
            }
744
745
            $id = $document_or_id->_id;
746
        } elseif (is_array($document_or_id)) {
747
            if (! isset($document_or_id['_id'])) {
748
                return null;
749
            }
750
751
            $id = $document_or_id['_id'];
752
        } else {
753
            $id = $document_or_id;
754
        }
755
756
        return MongoDBRef::create($this->name, $id);
757
    }
758
759
    /**
760
     * Fetches the document pointed to by a database reference
761
     *
762
     * @link http://www.php.net/manual/en/mongocollection.getdbref.php
763
     * @param array $ref A database reference.
764
     * @return array Returns the database document pointed to by the reference.
765
     */
766
    public function getDBRef(array $ref)
767
    {
768
        return $this->db->getDBRef($ref);
769
    }
770
771
    /**
772
     * Performs an operation similar to SQL's GROUP BY command
773
     *
774
     * @link http://www.php.net/manual/en/mongocollection.group.php
775
     * @param mixed $keys Fields to group by. If an array or non-code object is passed, it will be the key used to group results.
776
     * @param array $initial Initial value of the aggregation counter object.
777
     * @param MongoCode|string $reduce A function that aggregates (reduces) the objects iterated.
778
     * @param array $condition An condition that must be true for a row to be considered.
779
     * @return array
780
     */
781
    public function group($keys, array $initial, $reduce, array $condition = [])
782
    {
783
        if (is_string($reduce)) {
784
            $reduce = new MongoCode($reduce);
785
        }
786
787
        $command = [
788
            'group' => [
789
                'ns' => $this->name,
790
                '$reduce' => (string)$reduce,
791
                'initial' => $initial,
792
                'cond' => $condition,
793
            ],
794
        ];
795
796
        if ($keys instanceof MongoCode) {
797
            $command['group']['$keyf'] = (string)$keys;
798
        } else {
799
            $command['group']['key'] = $keys;
800
        }
801
        if (array_key_exists('condition', $condition)) {
802
            $command['group']['cond'] = $condition['condition'];
803
        }
804
        if (array_key_exists('finalize', $condition)) {
805
            if ($condition['finalize'] instanceof MongoCode) {
806
                $condition['finalize'] = (string)$condition['finalize'];
807
            }
808
            $command['group']['finalize'] = $condition['finalize'];
809
        }
810
811
        return $this->db->command($command);
812
    }
813
814
    /**
815
     * Returns an array of cursors to iterator over a full collection in parallel
816
     *
817
     * @link http://www.php.net/manual/en/mongocollection.parallelcollectionscan.php
818
     * @param int $num_cursors The number of cursors to request from the server. Please note, that the server can return less cursors than you requested.
819
     * @return MongoCommandCursor[]
820
     */
821
    public function parallelCollectionScan($num_cursors)
0 ignored issues
show
Unused Code introduced by
The parameter $num_cursors 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...
822
    {
823
        $this->notImplemented();
824
    }
825
826
    protected function notImplemented()
827
    {
828
        throw new \Exception('Not implemented');
829
    }
830
831
    /**
832
     * @return \MongoDB\Collection
833
     */
834 View Code Duplication
    private function createCollectionObject()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
835
    {
836
        $options = [
837
            'readPreference' => $this->readPreference,
838
            'writeConcern' => $this->writeConcern,
839
        ];
840
841
        if ($this->collection === null) {
842
            $this->collection = $this->db->getDb()->selectCollection($this->name, $options);
843
        } else {
844
            $this->collection = $this->collection->withOptions($options);
845
        }
846
    }
847
848
    /**
849
     * Converts legacy write concern options to a WriteConcern object
850
     *
851
     * @param array $options
852
     * @return array
853
     */
854
    private function convertWriteConcernOptions(array $options)
855
    {
856
        if (isset($options['safe'])) {
857
            $options['w'] = ($options['safe']) ? 1 : 0;
858
        }
859
860
        if (isset($options['wtimeout']) && !isset($options['wTimeoutMS'])) {
861
            $options['wTimeoutMS'] = $options['wtimeout'];
862
        }
863
864
        if (isset($options['w']) || !isset($options['wTimeoutMS'])) {
865
            $collectionWriteConcern = $this->getWriteConcern();
866
            $writeConcern = $this->createWriteConcernFromParameters(
867
                isset($options['w']) ? $options['w'] : $collectionWriteConcern['w'],
868
                isset($options['wTimeoutMS']) ? $options['wTimeoutMS'] : $collectionWriteConcern['wtimeout']
869
            );
870
871
            $options['writeConcern'] = $writeConcern;
872
        }
873
874
        unset($options['safe']);
875
        unset($options['w']);
876
        unset($options['wTimeout']);
877
        unset($options['wTimeoutMS']);
878
879
        return $options;
880
    }
881
882
    /**
883
     * @param array|object $document
884
     * @return MongoId
885
     */
886
    private function ensureDocumentHasMongoId(&$document)
887
    {
888
        $checkKeys = function($array) {
889
            foreach (array_keys($array) as $key) {
890
                if (empty($key) || strpos($key, '*') === 1) {
891
                    throw new \MongoException('document contain invalid key');
892
                }
893
            }
894
        };
895
896
        if (is_array($document)) {
897
            if (! isset($document['_id'])) {
898
                $document['_id'] = new \MongoId();
899
            }
900
901
            $checkKeys($document);
902
903
            return $document['_id'];
904
        } elseif (is_object($document)) {
905
            if (! isset($document->_id)) {
906
                $document->_id = new \MongoId();
907
            }
908
909
            $checkKeys((array) $document);
910
911
            return $document->_id;
912
        }
913
914
        return null;
915
    }
916
917
    private function checkCollectionName($name)
918
    {
919
        if (empty($name)) {
920
            throw new Exception('Collection name cannot be empty');
921
        } elseif (strpos($name, chr(0)) !== false) {
922
            throw new Exception('Collection name cannot contain null bytes');
923
        }
924
    }
925
}
926
927