Completed
Pull Request — master (#20)
by
unknown
04:34
created

MongoCollection::checkCollectionName()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 8
rs 9.4285
cc 3
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\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
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...
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 View Code Duplication
    public function __set($name, $value)
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...
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
        $this->validateDocument($a);
267
268
        if (! $this->ensureDocumentHasMongoId($a)) {
269
            trigger_error(sprintf('%s expects parameter %d to be an array or object, %s given', __METHOD__, 1, gettype($a)), E_WARNING);
270
            return;
271
        }
272
273
        if (! count((array)$a)) {
274
            throw new \MongoException('document must be an array or object');
275
        }
276
277
        try {
278
            $result = $this->collection->insertOne(
279
                TypeConverter::fromLegacy($a),
280
                $this->convertWriteConcernOptions($options)
281
            );
282
        } catch (\MongoDB\Driver\Exception\BulkWriteException $e) {
1 ignored issue
show
Bug introduced by
The class MongoDB\Driver\Exception\BulkWriteException 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...
283
            $writeResult = $e->getWriteResult();
284
            $writeError = $writeResult->getWriteErrors()[0];
285
            return [
286
                'ok' => 0.0,
287
                'n' => 0,
288
                'err' => $writeError->getCode(),
289
                'errmsg' => $writeError->getMessage(),
290
            ];
291
        } 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...
292
            ExceptionConverter::toLegacy($e);
293
        }
294
295
        if (! $result->isAcknowledged()) {
296
            return true;
297
        }
298
299
        return [
300
            'ok' => 1.0,
301
            'n' => 0,
302
            'err' => null,
303
            'errmsg' => null,
304
        ];
305
    }
306
307
    /**
308
     * Inserts multiple documents into this collection
309
     *
310
     * @link http://www.php.net/manual/en/mongocollection.batchinsert.php
311
     * @param array $a An array of arrays.
312
     * @param array $options Options for the inserts.
313
     * @throws MongoCursorException
314
     * @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.
315
     */
316
    public function batchInsert(array &$a, array $options = [])
317
    {
318
        foreach ($a as $key => $item) {
319
            $this->validateDocument($a[$key]);
320
            $this->ensureDocumentHasMongoId($a[$key]);
321
        }
322
323
        try {
324
            $result = $this->collection->insertMany(
325
                TypeConverter::fromLegacy(array_values($a)),
326
                $this->convertWriteConcernOptions($options)
327
            );
328
        } 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...
329
            ExceptionConverter::toLegacy($e);
330
        }
331
332
        if (! $result->isAcknowledged()) {
333
            return true;
334
        }
335
336
        return [
337
            'connectionId' => 0,
338
            'n' => 0,
339
            'syncMillis' => 0,
340
            'writtenTo' => null,
341
            'err' => null,
342
            'errmsg' => null,
343
        ];
344
    }
345
346
    /**
347
     * Update records based on a given criteria
348
     *
349
     * @link http://www.php.net/manual/en/mongocollection.update.php
350
     * @param array $criteria Description of the objects to update.
351
     * @param array $newobj The object with which to update the matching records.
352
     * @param array $options
353
     * @throws MongoCursorException
354
     * @return boolean
355
     */
356
    public function update(array $criteria , array $newobj, array $options = [])
357
    {
358
        $multiple = isset($options['multiple']) ? $options['multiple'] : false;
359
        $method = $multiple ? 'updateMany' : 'updateOne';
360
        unset($options['multiple']);
361
362
        try {
363
            /** @var \MongoDB\UpdateResult $result */
364
            $result = $this->collection->$method(
365
                TypeConverter::fromLegacy($criteria),
366
                TypeConverter::fromLegacy($newobj),
367
                $this->convertWriteConcernOptions($options)
368
            );
369
        } catch (\MongoDB\Driver\Exception\BulkWriteException $e) {
1 ignored issue
show
Bug introduced by
The class MongoDB\Driver\Exception\BulkWriteException 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...
370
            $writeResult = $e->getWriteResult();
371
            $writeError = $writeResult->getWriteErrors()[0];
372
            return [
373
                'ok' => 0.0,
374
                'nModified' => $writeResult->getModifiedCount(),
375
                'n' => $writeResult->getMatchedCount(),
376
                'err' => $writeError->getCode(),
377
                'errmsg' => $writeError->getMessage(),
378
                'updatedExisting' => $writeResult->getUpsertedCount() == 0,
379
            ];
380
        } 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...
381
            ExceptionConverter::toLegacy($e);
382
        }
383
384
        if (! $result->isAcknowledged()) {
385
            return true;
386
        }
387
388
        return [
389
            'ok' => 1.0,
390
            'nModified' => $result->getModifiedCount(),
391
            'n' => $result->getMatchedCount(),
392
            'err' => null,
393
            'errmsg' => null,
394
            'updatedExisting' => $result->getUpsertedCount() == 0,
395
        ];
396
    }
397
398
    /**
399
     * Remove records from this collection
400
     *
401
     * @link http://www.php.net/manual/en/mongocollection.remove.php
402
     * @param array $criteria Query criteria for the documents to delete.
403
     * @param array $options An array of options for the remove operation.
404
     * @throws MongoCursorException
405
     * @throws MongoCursorTimeoutException
406
     * @return bool|array Returns an array containing the status of the removal
407
     * if the "w" option is set. Otherwise, returns TRUE.
408
     */
409
    public function remove(array $criteria = [], array $options = [])
410
    {
411
        $multiple = isset($options['justOne']) ? !$options['justOne'] : true;
412
        $method = $multiple ? 'deleteMany' : 'deleteOne';
413
414
        try {
415
            /** @var \MongoDB\DeleteResult $result */
416
            $result = $this->collection->$method(
417
                TypeConverter::fromLegacy($criteria),
418
                $this->convertWriteConcernOptions($options)
419
            );
420
        } 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...
421
            ExceptionConverter::toLegacy($e);
422
        }
423
424
        if (! $result->isAcknowledged()) {
425
            return true;
426
        }
427
428
        return [
429
            'ok' => 1.0,
430
            'n' => $result->getDeletedCount(),
431
            'err' => null,
432
            'errmsg' => null
433
        ];
434
    }
435
436
    /**
437
     * Querys this collection
438
     *
439
     * @link http://www.php.net/manual/en/mongocollection.find.php
440
     * @param array $query The fields for which to search.
441
     * @param array $fields Fields of the results to return.
442
     * @return MongoCursor
443
     */
444 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...
445
    {
446
        $cursor = new MongoCursor($this->db->getConnection(), (string) $this, $query, $fields);
447
        $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...
448
449
        return $cursor;
450
    }
451
452
    /**
453
     * Retrieve a list of distinct values for the given key across a collection
454
     *
455
     * @link http://www.php.net/manual/ru/mongocollection.distinct.php
456
     * @param string $key The key to use.
457
     * @param array $query An optional query parameters
458
     * @return array|bool Returns an array of distinct values, or FALSE on failure
459
     */
460
    public function distinct($key, array $query = [])
461
    {
462
        try {
463
            return array_map([TypeConverter::class, 'toLegacy'], $this->collection->distinct($key, $query));
464
        } 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...
465
            return false;
466
        }
467
    }
468
469
    /**
470
     * Update a document and return it
471
     *
472
     * @link http://www.php.net/manual/ru/mongocollection.findandmodify.php
473
     * @param array $query The query criteria to search for.
474
     * @param array $update The update criteria.
475
     * @param array $fields Optionally only return these fields.
476
     * @param array $options An array of options to apply, such as remove the match document from the DB and return it.
477
     * @return array Returns the original document, or the modified document when new is set.
478
     */
479
    public function findAndModify(array $query, array $update = null, array $fields = null, array $options = [])
480
    {
481
        $query = TypeConverter::fromLegacy($query);
482
        try {
483
            if (isset($options['remove'])) {
484
                unset($options['remove']);
485
                $document = $this->collection->findOneAndDelete($query, $options);
486
            } else {
487
                $update = is_array($update) ? TypeConverter::fromLegacy($update) : [];
488
489
                if (isset($options['new'])) {
490
                    $options['returnDocument'] = \MongoDB\Operation\FindOneAndUpdate::RETURN_DOCUMENT_AFTER;
491
                    unset($options['new']);
492
                }
493
494
                $options['projection'] = is_array($fields) ? TypeConverter::fromLegacy($fields) : [];
495
496
                $document = $this->collection->findOneAndUpdate($query, $update, $options);
497
            }
498
        } 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...
499
            throw new MongoResultException($e->getMessage(), $e->getCode(), $e);
500
        } 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...
501
            ExceptionConverter::toLegacy($e);
502
        }
503
504
        if ($document) {
505
            $document = TypeConverter::toLegacy($document);
0 ignored issues
show
Bug introduced by
The variable $document does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
506
        }
507
508
        return $document;
509
    }
510
511
    /**
512
     * Querys this collection, returning a single element
513
     *
514
     * @link http://www.php.net/manual/en/mongocollection.findone.php
515
     * @param array $query The fields for which to search.
516
     * @param array $fields Fields of the results to return.
517
     * @param array $options
518
     * @return array|null
519
     */
520
    public function findOne(array $query = [], array $fields = [], array $options = [])
521
    {
522
        $options = ['projection' => $fields] + $options;
523
        try {
524
            $document = $this->collection->findOne(TypeConverter::fromLegacy($query), $options);
525
        } 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...
526
            ExceptionConverter::toLegacy($e);
527
        }
528
529
        if ($document !== null) {
530
            $document = TypeConverter::toLegacy($document);
531
        }
532
533
        return $document;
534
    }
535
536
    /**
537
     * Creates an index on the given field(s), or does nothing if the index already exists
538
     *
539
     * @link http://www.php.net/manual/en/mongocollection.createindex.php
540
     * @param array $keys Field or fields to use as index.
541
     * @param array $options [optional] This parameter is an associative array of the form array("optionname" => <boolean>, ...).
542
     * @return array Returns the database response.
543
     *
544
     * @todo This method does not yet return the correct result
545
     */
546
    public function createIndex(array $keys, array $options = [])
547
    {
548
        if (! is_array($keys) || ! count($keys)) {
549
            throw new MongoException('keys cannot be empty');
550
        }
551
552
        // duplicate
553
        $neededOptions = ['unique' => 1, 'sparse' => 1, 'expireAfterSeconds' => 1, 'background' => 1, 'dropDups' => 1];
554
        $indexOptions = array_intersect_key($options, $neededOptions);
555
        $indexes = $this->collection->listIndexes();
556
        foreach ($indexes as $index) {
557
558
            if (! empty($options['name']) && $index->getName() === $options['name']) {
559
                throw new \MongoDuplicateKeyException();
560
            }
561
562
            if ($index->getKey() == $keys) {
563
                $currentIndexOptions = array_intersect_key($index->__debugInfo(), $neededOptions);
564
565
                unset($currentIndexOptions['name']);
566
                if ($currentIndexOptions != $indexOptions) {
567
                    throw new \MongoDuplicateKeyException();
568
                }
569
570
                return [
571
                    'createdCollectionAutomatically' => false,
572
                    'numIndexesBefore' => count($indexes),
573
                    'numIndexesAfter' => count($indexes),
574
                    'note' => 'all indexes already exist',
575
                    'ok' => 1.0
576
                ];
577
            }
578
        }
579
580
        try {
581
            $this->collection->createIndex($keys, $this->convertWriteConcernOptions($options));
582
        } 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...
583
            ExceptionConverter::toLegacy($e);
584
        }
585
586
        return [
587
            'createdCollectionAutomatically' => true,
588
            'numIndexesBefore' => count($indexes),
589
            'numIndexesAfter' => count($indexes) + 1,
590
            'ok' => 1.0
591
        ];
592
    }
593
594
    /**
595
     * Creates an index on the given field(s), or does nothing if the index already exists
596
     *
597
     * @link http://www.php.net/manual/en/mongocollection.ensureindex.php
598
     * @param array $keys Field or fields to use as index.
599
     * @param array $options [optional] This parameter is an associative array of the form array("optionname" => <boolean>, ...).
600
     * @return boolean always true
601
     * @deprecated Use MongoCollection::createIndex() instead.
602
     */
603
    public function ensureIndex(array $keys, array $options = [])
604
    {
605
        $this->createIndex($keys, $options);
606
607
        return true;
608
    }
609
610
    /**
611
     * Deletes an index from this collection
612
     *
613
     * @link http://www.php.net/manual/en/mongocollection.deleteindex.php
614
     * @param string|array $keys Field or fields from which to delete the index.
615
     * @return array Returns the database response.
616
     */
617
    public function deleteIndex($keys)
618
    {
619
        if (is_string($keys)) {
620
            $indexName = $keys;
621
        } elseif (is_array($keys)) {
622
            $indexName = \MongoDB\generate_index_name($keys);
623
        } else {
624
            throw new \InvalidArgumentException();
625
        }
626
627
        return TypeConverter::toLegacy($this->collection->dropIndex($indexName));
628
    }
629
630
    /**
631
     * Delete all indexes for this collection
632
     *
633
     * @link http://www.php.net/manual/en/mongocollection.deleteindexes.php
634
     * @return array Returns the database response.
635
     */
636
    public function deleteIndexes()
637
    {
638
        return TypeConverter::toLegacy($this->collection->dropIndexes());
639
    }
640
641
    /**
642
     * Returns an array of index names for this collection
643
     *
644
     * @link http://www.php.net/manual/en/mongocollection.getindexinfo.php
645
     * @return array Returns a list of index names.
646
     */
647
    public function getIndexInfo()
648
    {
649
        $convertIndex = function(\MongoDB\Model\IndexInfo $indexInfo) {
650
            return [
651
                'v' => $indexInfo->getVersion(),
652
                'key' => $indexInfo->getKey(),
653
                'name' => $indexInfo->getName(),
654
                'ns' => $indexInfo->getNamespace(),
655
            ];
656
        };
657
658
        return array_map($convertIndex, iterator_to_array($this->collection->listIndexes()));
659
    }
660
661
    /**
662
     * Counts the number of documents in this collection
663
     *
664
     * @link http://www.php.net/manual/en/mongocollection.count.php
665
     * @param array|stdClass $query
666
     * @param array $options
667
     * @return int Returns the number of documents matching the query.
668
     */
669
    public function count($query = [], array $options = [])
670
    {
671
        try {
672
            return $this->collection->count(TypeConverter::fromLegacy($query), $options);
673
        } 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...
674
            ExceptionConverter::toLegacy($e);
675
        }
676
    }
677
678
    /**
679
     * Saves an object to this collection
680
     *
681
     * @link http://www.php.net/manual/en/mongocollection.save.php
682
     * @param array|object $a Array to save. If an object is used, it may not have protected or private properties.
683
     * @param array $options Options for the save.
684
     * @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.
685
     * @throws MongoCursorException if the "w" option is set and the write fails.
686
     * @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.
687
     * @return array|boolean If w was set, returns an array containing the status of the save.
688
     * Otherwise, returns a boolean representing if the array was not empty (an empty array will not be inserted).
689
     */
690
    public function save(&$a, array $options = [])
691
    {
692
        $this->validateDocument($a);
693
694
        $id = $this->ensureDocumentHasMongoId($a);
695
696
        $document = (array) $a;
697
        unset($document['_id']);
698
699
        $options['upsert'] = true;
700
701
        $result = $this->update(['_id' => $id], ['$set' => $a], $options);
702
        if ($result['ok'] == 0.0) {
703
            throw new \MongoCursorException();
704
        }
705
706
        return $result;
707
    }
708
709
    /**
710
     * Creates a database reference
711
     *
712
     * @link http://www.php.net/manual/en/mongocollection.createdbref.php
713
     * @param array|object $document_or_id Object to which to create a reference.
714
     * @return array Returns a database reference array.
715
     */
716 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...
717
    {
718
        if ($document_or_id instanceof \MongoId) {
719
            $id = $document_or_id;
720
        } elseif (is_object($document_or_id)) {
721
            if (! isset($document_or_id->_id)) {
722
                return null;
723
            }
724
725
            $id = $document_or_id->_id;
726
        } elseif (is_array($document_or_id)) {
727
            if (! isset($document_or_id['_id'])) {
728
                return null;
729
            }
730
731
            $id = $document_or_id['_id'];
732
        } else {
733
            $id = $document_or_id;
734
        }
735
736
        return MongoDBRef::create($this->name, $id);
737
    }
738
739
    /**
740
     * Fetches the document pointed to by a database reference
741
     *
742
     * @link http://www.php.net/manual/en/mongocollection.getdbref.php
743
     * @param array $ref A database reference.
744
     * @return array Returns the database document pointed to by the reference.
745
     */
746
    public function getDBRef(array $ref)
747
    {
748
        return $this->db->getDBRef($ref);
749
    }
750
751
    /**
752
     * Performs an operation similar to SQL's GROUP BY command
753
     *
754
     * @link http://www.php.net/manual/en/mongocollection.group.php
755
     * @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.
756
     * @param array $initial Initial value of the aggregation counter object.
757
     * @param MongoCode|string $reduce A function that aggregates (reduces) the objects iterated.
758
     * @param array $condition An condition that must be true for a row to be considered.
759
     * @return array
760
     */
761
    public function group($keys, array $initial, $reduce, array $condition = [])
762
    {
763
        if (is_string($reduce)) {
764
            $reduce = new MongoCode($reduce);
765
        }
766
767
        $command = [
768
            'group' => [
769
                'ns' => $this->name,
770
                '$reduce' => (string)$reduce,
771
                'initial' => $initial,
772
                'cond' => $condition,
773
            ],
774
        ];
775
776
        if ($keys instanceof MongoCode) {
777
            $command['group']['$keyf'] = (string)$keys;
778
        } else {
779
            $command['group']['key'] = $keys;
780
        }
781
        if (array_key_exists('condition', $condition)) {
782
            $command['group']['cond'] = $condition['condition'];
783
        }
784
        if (array_key_exists('finalize', $condition)) {
785
            if ($condition['finalize'] instanceof MongoCode) {
786
                $condition['finalize'] = (string)$condition['finalize'];
787
            }
788
            $command['group']['finalize'] = $condition['finalize'];
789
        }
790
791
        return $this->db->command($command);
792
    }
793
794
    /**
795
     * Returns an array of cursors to iterator over a full collection in parallel
796
     *
797
     * @link http://www.php.net/manual/en/mongocollection.parallelcollectionscan.php
798
     * @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.
799
     * @return MongoCommandCursor[]
800
     */
801
    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...
802
    {
803
        $this->notImplemented();
804
    }
805
806
    protected function notImplemented()
807
    {
808
        throw new \Exception('Not implemented');
809
    }
810
811
    /**
812
     * @return \MongoDB\Collection
813
     */
814 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...
815
    {
816
        $options = [
817
            'readPreference' => $this->readPreference,
818
            'writeConcern' => $this->writeConcern,
819
        ];
820
821
        if ($this->collection === null) {
822
            $this->collection = $this->db->getDb()->selectCollection($this->name, $options);
823
        } else {
824
            $this->collection = $this->collection->withOptions($options);
825
        }
826
    }
827
828
    /**
829
     * Converts legacy write concern options to a WriteConcern object
830
     *
831
     * @param array $options
832
     * @return array
833
     */
834
    private function convertWriteConcernOptions(array $options)
835
    {
836
        if (isset($options['safe'])) {
837
            $options['w'] = ($options['safe']) ? 1 : 0;
838
        }
839
840
        if (isset($options['wtimeout']) && !isset($options['wTimeoutMS'])) {
841
            $options['wTimeoutMS'] = $options['wtimeout'];
842
        }
843
844
        if (isset($options['w']) || !isset($options['wTimeoutMS'])) {
845
            $collectionWriteConcern = $this->getWriteConcern();
846
            $writeConcern = $this->createWriteConcernFromParameters(
847
                isset($options['w']) ? $options['w'] : $collectionWriteConcern['w'],
848
                isset($options['wTimeoutMS']) ? $options['wTimeoutMS'] : $collectionWriteConcern['wtimeout']
849
            );
850
851
            $options['writeConcern'] = $writeConcern;
852
        }
853
854
        unset($options['safe']);
855
        unset($options['w']);
856
        unset($options['wTimeout']);
857
        unset($options['wTimeoutMS']);
858
859
        return $options;
860
    }
861
862
    /**
863
     * @param array|object $document
864
     * @return MongoId
865
     */
866
    private function ensureDocumentHasMongoId(&$document)
867
    {
868
        if (is_array($document)) {
869
            if (! isset($document['_id'])) {
870
                $document['_id'] = new \MongoId();
871
            }
872
873
            return $document['_id'];
874
        } elseif (is_object($document)) {
875
            if (! isset($document->_id)) {
876
                $document->_id = new \MongoId();
877
            }
878
879
            return $document->_id;
880
        }
881
882
        return null;
883
    }
884
885
    private function validateDocument(&$document)
886
    {
887
        if (! is_array($document) && ! is_object($document)) {
888
            throw new \MongoException('document must be an array or object');
889
        }
890
        $array = (array) $document;
891
        if (empty($array)) {
892
            throw new \MongoException('document cannot be empty');
893
        }
894
895
        foreach (array_keys($array) as $key) {
896
            if (is_int($key) || empty($key) || strpos($key, '*') === 1) {
897
                throw new \MongoException('document contain invalid key');
898
            }
899
        }
900
    }
901
902
    private function checkCollectionName($name)
903
    {
904
        if (empty($name)) {
905
            throw new Exception('Collection name cannot be empty');
906
        } elseif (strpos($name, chr(0)) !== false) {
907
            throw new Exception('Collection name cannot contain null bytes');
908
        }
909
    }
910
}
911
912