Completed
Pull Request — master (#20)
by
unknown
03:43
created

MongoDB::checkDatabaseName()   B

Complexity

Conditions 6
Paths 6

Size

Total Lines 20
Code Lines 11

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 20
rs 8.8571
cc 6
eloc 11
nc 6
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
use MongoDB\Model\CollectionInfo;
20
21
/**
22
 * Instances of this class are used to interact with a database.
23
 * @link http://www.php.net/manual/en/class.mongodb.php
24
 */
25
class MongoDB
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...
26
{
27
    use Helper\ReadPreference;
28
    use Helper\SlaveOkay;
29
    use Helper\WriteConcern;
30
31
    const PROFILING_OFF = 0;
32
    const PROFILING_SLOW = 1;
33
    const PROFILING_ON = 2;
34
35
    /**
36
     * @var MongoClient
37
     */
38
    protected $connection;
39
40
    /**
41
     * @var \MongoDB\Database
42
     */
43
    protected $db;
44
45
    /**
46
     * @var string
47
     */
48
    protected $name;
49
50
    /**
51
     * Creates a new database
52
     *
53
     * This method is not meant to be called directly. The preferred way to create an instance of MongoDB is through {@see Mongo::__get()} or {@see Mongo::selectDB()}.
54
     * @link http://www.php.net/manual/en/mongodb.construct.php
55
     * @param MongoClient $conn Database connection.
56
     * @param string $name Database name.
57
     * @throws Exception
58
     * @return MongoDB Returns the database.
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...
59
     */
60
    public function __construct(MongoClient $conn, $name)
61
    {
62
        $this->checkDatabaseName($name);
63
        $this->connection = $conn;
64
        $this->name = $name;
65
66
        $this->setReadPreferenceFromArray($conn->getReadPreference());
67
        $this->setWriteConcernFromArray($conn->getWriteConcern());
68
69
        $this->createDatabaseObject();
70
    }
71
72
    /**
73
     * @return \MongoDB\Database
74
     * @internal This method is not part of the ext-mongo API
75
     */
76
    public function getDb()
77
    {
78
        return $this->db;
79
    }
80
81
    /**
82
     * The name of this database
83
     *
84
     * @link http://www.php.net/manual/en/mongodb.--tostring.php
85
     * @return string Returns this database's name.
86
     */
87
    public function __toString()
88
    {
89
        return $this->name;
90
    }
91
92
    /**
93
     * Gets a collection
94
     *
95
     * @link http://www.php.net/manual/en/mongodb.get.php
96
     * @param string $name The name of the collection.
97
     * @return MongoCollection
98
     */
99
    public function __get($name)
100
    {
101
        // Handle w and wtimeout properties that replicate data stored in $readPreference
102
        if ($name === 'w' || $name === 'wtimeout') {
103
            return $this->getWriteConcern()[$name];
104
        }
105
106
        return $this->selectCollection($name);
107
    }
108
109
    /**
110
     * @param string $name
111
     * @param mixed $value
112
     */
113 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...
114
    {
115
        if ($name === 'w' || $name === 'wtimeout') {
116
            $this->setWriteConcernFromArray([$name => $value] + $this->getWriteConcern());
117
            $this->createDatabaseObject();
118
        }
119
    }
120
121
    /**
122
     * Returns information about collections in this database
123
     *
124
     * @link http://www.php.net/manual/en/mongodb.getcollectioninfo.php
125
     * @param array $options An array of options for listing the collections.
126
     * @return array
127
     */
128
    public function getCollectionInfo(array $options = [])
129
    {
130
        // The includeSystemCollections option is no longer supported
131
        if (isset($options['includeSystemCollections'])) {
132
            unset($options['includeSystemCollections']);
133
        }
134
135
        try {
136
            $collections = $this->db->listCollections($options);
137
        } 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...
138
            ExceptionConverter::toLegacy($e);
139
        }
140
141
        $getCollectionInfo = function (CollectionInfo $collectionInfo) {
142
            return [
143
                'name' => $collectionInfo->getName(),
144
                'options' => $collectionInfo->getOptions(),
145
            ];
146
        };
147
148
        return array_map($getCollectionInfo, iterator_to_array($collections));
149
    }
150
151
    /**
152
     * Get all collections from this database
153
     *
154
     * @link http://www.php.net/manual/en/mongodb.getcollectionnames.php
155
     * @param array $options An array of options for listing the collections.
156
     * @return array Returns the names of the all the collections in the database as an array
157
     */
158
    public function getCollectionNames(array $options = [])
159
    {
160
        // The includeSystemCollections option is no longer supported
161
        if (isset($options['includeSystemCollections'])) {
162
            unset($options['includeSystemCollections']);
163
        }
164
165
        try {
166
            $collections = $this->db->listCollections($options);
167
        } 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...
168
            ExceptionConverter::toLegacy($e);
169
        }
170
171
        $getCollectionName = function (CollectionInfo $collectionInfo) {
172
            return $collectionInfo->getName();
173
        };
174
175
        return array_map($getCollectionName, iterator_to_array($collections));
176
    }
177
178
    /**
179
     * @return MongoClient
180
     * @internal This method is not part of the ext-mongo API
181
     */
182
    public function getConnection()
183
    {
184
        return $this->connection;
185
    }
186
187
    /**
188
     * Fetches toolkit for dealing with files stored in this database
189
     *
190
     * @link http://www.php.net/manual/en/mongodb.getgridfs.php
191
     * @param string $prefix The prefix for the files and chunks collections.
192
     * @return MongoGridFS Returns a new gridfs object for this database.
193
     */
194
    public function getGridFS($prefix = "fs")
195
    {
196
        return new \MongoGridFS($this, $prefix);
197
    }
198
199
    /**
200
     * Gets this database's profiling level
201
     *
202
     * @link http://www.php.net/manual/en/mongodb.getprofilinglevel.php
203
     * @return int Returns the profiling level.
204
     */
205
    public function getProfilingLevel()
206
    {
207
        $result = $this->command(['profile' => -1]);
208
209
        return ($result['ok'] && isset($result['was'])) ? $result['was'] : 0;
210
    }
211
212
    /**
213
     * Sets this database's profiling level
214
     *
215
     * @link http://www.php.net/manual/en/mongodb.setprofilinglevel.php
216
     * @param int $level Profiling level.
217
     * @return int Returns the previous profiling level.
218
     */
219
    public function setProfilingLevel($level)
220
    {
221
        $result = $this->command(['profile' => $level]);
222
223
        return ($result['ok'] && isset($result['was'])) ? $result['was'] : 0;
224
    }
225
226
    /**
227
     * Drops this database
228
     *
229
     * @link http://www.php.net/manual/en/mongodb.drop.php
230
     * @return array Returns the database response.
231
     */
232
    public function drop()
233
    {
234
        return TypeConverter::toLegacy($this->db->drop());
235
    }
236
237
    /**
238
     * Repairs and compacts this database
239
     *
240
     * @link http://www.php.net/manual/en/mongodb.repair.php
241
     * @param bool $preserve_cloned_files [optional] <p>If cloned files should be kept if the repair fails.</p>
242
     * @param bool $backup_original_files [optional] <p>If original files should be backed up.</p>
243
     * @return array <p>Returns db response.</p>
244
     */
245
    public function repair($preserve_cloned_files = FALSE, $backup_original_files = FALSE)
0 ignored issues
show
Unused Code introduced by
The parameter $preserve_cloned_files is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $backup_original_files 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...
246
    {
247
        $this->notImplemented();
248
    }
249
250
    /**
251
     * Gets a collection
252
     *
253
     * @link http://www.php.net/manual/en/mongodb.selectcollection.php
254
     * @param string $name <b>The collection name.</b>
255
     * @throws Exception if the collection name is invalid.
256
     * @return MongoCollection Returns a new collection object.
257
     */
258
    public function selectCollection($name)
259
    {
260
        return new MongoCollection($this, $name);
261
    }
262
263
    /**
264
     * Creates a collection
265
     *
266
     * @link http://www.php.net/manual/en/mongodb.createcollection.php
267
     * @param string $name The name of the collection.
268
     * @param array $options
269
     * @return MongoCollection Returns a collection object representing the new collection.
270
     */
271
    public function createCollection($name, $options)
272
    {
273
        try {
274
            $this->db->createCollection($name, $options);
275
        } 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...
276
            return false;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return false; (false) is incompatible with the return type documented by MongoDB::createCollection of type MongoCollection.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
277
        }
278
279
        return $this->selectCollection($name);
280
    }
281
282
    /**
283
     * Drops a collection
284
     *
285
     * @link http://www.php.net/manual/en/mongodb.dropcollection.php
286
     * @param MongoCollection|string $coll MongoCollection or name of collection to drop.
287
     * @return array Returns the database response.
288
     *
289
     * @deprecated Use MongoCollection::drop() instead.
290
     */
291
    public function dropCollection($coll)
292
    {
293
        if ($coll instanceof MongoCollection) {
294
            $coll = $coll->getName();
295
        }
296
297
        return TypeConverter::toLegacy($this->db->dropCollection((string) $coll));
298
    }
299
300
    /**
301
     * Get a list of collections in this database
302
     *
303
     * @link http://www.php.net/manual/en/mongodb.listcollections.php
304
     * @param array $options
305
     * @return MongoCollection[] Returns a list of MongoCollections.
306
     */
307
    public function listCollections(array $options = [])
308
    {
309
        return array_map([$this, 'selectCollection'], $this->getCollectionNames($options));
310
    }
311
312
    /**
313
     * Creates a database reference
314
     *
315
     * @link http://www.php.net/manual/en/mongodb.createdbref.php
316
     * @param string $collection The collection to which the database reference will point.
317
     * @param mixed $document_or_id
318
     * @return array Returns a database reference array.
319
     */
320 View Code Duplication
    public function createDBRef($collection, $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...
321
    {
322
        if ($document_or_id instanceof \MongoId) {
323
            $id = $document_or_id;
324
        } elseif (is_object($document_or_id)) {
325
            if (! isset($document_or_id->_id)) {
326
                return null;
327
            }
328
329
            $id = $document_or_id->_id;
330
        } elseif (is_array($document_or_id)) {
331
            if (! isset($document_or_id['_id'])) {
332
                return null;
333
            }
334
335
            $id = $document_or_id['_id'];
336
        } else {
337
            $id = $document_or_id;
338
        }
339
340
        return MongoDBRef::create($collection, $id, $this->name);
341
    }
342
343
344
    /**
345
     * Fetches the document pointed to by a database reference
346
     *
347
     * @link http://www.php.net/manual/en/mongodb.getdbref.php
348
     * @param array $ref A database reference.
349
     * @return array Returns the document pointed to by the reference.
350
     */
351
    public function getDBRef(array $ref)
352
    {
353
        return MongoDBRef::get($this, $ref);
354
    }
355
356
    /**
357
     * Runs JavaScript code on the database server.
358
     *
359
     * @link http://www.php.net/manual/en/mongodb.execute.php
360
     * @param MongoCode|string $code Code to execute.
361
     * @param array $args [optional] Arguments to be passed to code.
362
     * @return array Returns the result of the evaluation.
363
     */
364
    public function execute($code, array $args = [])
365
    {
366
        return $this->command(['eval' => $code, 'args' => $args]);
367
    }
368
369
    /**
370
     * Execute a database command
371
     *
372
     * @link http://www.php.net/manual/en/mongodb.command.php
373
     * @param array $data The query to send.
374
     * @param array $options
375
     * @return array Returns database response.
376
     */
377
    public function command(array $data, $options = [], &$hash = null)
0 ignored issues
show
Unused Code introduced by
The parameter $options is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $hash 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...
378
    {
379
        try {
380
            $cursor = new \MongoCommandCursor($this->connection, $this->name, $data);
381
            $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...
382
383
            return iterator_to_array($cursor)[0];
384
        } catch (\MongoDB\Driver\Exception\ExecutionTimeoutException $e) {
1 ignored issue
show
Bug introduced by
The class MongoDB\Driver\Exception\ExecutionTimeoutException does not exist. Did you forget a USE statement, or did you not list all dependencies?

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

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

Loading history...
385
            throw new MongoCursorTimeoutException($e->getMessage(), $e->getCode(), $e);
386
        } catch (\MongoDB\Driver\Exception\RuntimeException $e) {
0 ignored issues
show
Bug introduced by
The class MongoDB\Driver\Exception\RuntimeException 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...
387
            return [
388
                'ok' => 0,
389
                'errmsg' => $e->getMessage(),
390
                'code' => $e->getCode(),
391
            ];
392
        } catch (\MongoDB\Driver\Exception\Excepiton $e) {
0 ignored issues
show
Bug introduced by
The class MongoDB\Driver\Exception\Excepiton 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...
393
            ExceptionConverter::toLegacy($e);
394
        }
395
    }
396
397
    /**
398
     * Check if there was an error on the most recent db operation performed
399
     *
400
     * @link http://www.php.net/manual/en/mongodb.lasterror.php
401
     * @return array Returns the error, if there was one.
402
     */
403
    public function lastError()
404
    {
405
        return $this->command(array('getLastError' => 1));
406
    }
407
408
    /**
409
     * Checks for the last error thrown during a database operation
410
     *
411
     * @link http://www.php.net/manual/en/mongodb.preverror.php
412
     * @return array Returns the error and the number of operations ago it occurred.
413
     */
414
    public function prevError()
415
    {
416
        return $this->command(array('getPrevError' => 1));
417
    }
418
419
    /**
420
     * Clears any flagged errors on the database
421
     *
422
     * @link http://www.php.net/manual/en/mongodb.reseterror.php
423
     * @return array Returns the database response.
424
     */
425
    public function resetError()
426
    {
427
        return $this->command(array('resetError' => 1));
428
    }
429
430
    /**
431
     * Creates a database error
432
     *
433
     * @link http://www.php.net/manual/en/mongodb.forceerror.php
434
     * @return boolean Returns the database response.
435
     */
436
    public function forceError()
437
    {
438
        return $this->command(array('forceerror' => 1));
439
    }
440
441
    /**
442
     * Log in to this database
443
     *
444
     * @link http://www.php.net/manual/en/mongodb.authenticate.php
445
     * @param string $username The username.
446
     * @param string $password The password (in plaintext).
447
     * @return array Returns database response. If the login was successful, it will return 1.
448
     *
449
     * @deprecated This method is not implemented, supply authentication credentials through the connection string instead.
450
     */
451
    public function authenticate($username, $password)
0 ignored issues
show
Unused Code introduced by
The parameter $username is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $password 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...
452
    {
453
        throw new \Exception('The MongoDB::authenticate method is not supported. Please supply authentication credentials through the connection string');
454
    }
455
456
    /**
457
     * {@inheritdoc}
458
     */
459
    public function setReadPreference($readPreference, $tags = null)
460
    {
461
        $result = $this->setReadPreferenceFromParameters($readPreference, $tags);
462
        $this->createDatabaseObject();
463
464
        return $result;
465
    }
466
467
    /**
468
     * {@inheritdoc}
469
     */
470
    public function setWriteConcern($wstring, $wtimeout = 0)
471
    {
472
        $result = $this->setWriteConcernFromParameters($wstring, $wtimeout);
473
        $this->createDatabaseObject();
474
475
        return $result;
476
    }
477
478
    protected function notImplemented()
479
    {
480
        throw new \Exception('Not implemented');
481
    }
482
483
    /**
484
     * @return \MongoDB\Database
485
     */
486 View Code Duplication
    private function createDatabaseObject()
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...
487
    {
488
        $options = [
489
            'readPreference' => $this->readPreference,
490
            'writeConcern' => $this->writeConcern,
491
        ];
492
493
        if ($this->db === null) {
494
            $this->db = $this->connection->getClient()->selectDatabase($this->name, $options);
495
        } else {
496
            $this->db = $this->db->withOptions($options);
497
        }
498
    }
499
500
    private function checkDatabaseName($name)
501
    {
502
        if (empty($name)) {
503
            throw new \Exception('Database name cannot be empty');
504
        }
505
        if (strlen($name) >= 64) {
506
            throw new \Exception('Database name cannot exceed 63 characters');
507
        }
508
        if (strpos($name, chr(0)) !== false) {
509
            throw new \Exception('Database name cannot contain null bytes');
510
        }
511
512
        $invalidCharacters = ['.', '$', '/', ' ', '\\'];
513
        foreach ($invalidCharacters as $char) {
514
            if (strchr($name, $char) !== false) {
515
                throw new \Exception('Database name contains invalid characters');
516
            }
517
        }
518
519
    }
520
}
521