Completed
Push — master ( ddd4fc...065b3a )
by Andreas
25:59
created

MongoClient::applyConnectionOptions()   F

Complexity

Conditions 14
Paths 360

Size

Total Lines 39
Code Lines 23

Duplication

Lines 4
Ratio 10.26 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 14
eloc 23
nc 360
nop 2
dl 4
loc 39
rs 3.7522
c 1
b 0
f 0

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
1 ignored issue
show
Coding Style Compatibility introduced by
For compatibility and reusability of your code, PSR1 recommends that a file should introduce either new symbols (like classes, functions, etc.) or have side-effects (like outputting something, or including other files), but not both at the same time. The first symbol is defined on line 29 and the first side effect is on line 17.

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

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

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

Loading history...
2
/*
3
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
4
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
5
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
6
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
7
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
8
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
9
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
10
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
11
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
12
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
13
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
14
 */
15
16
if (class_exists('MongoClient', false)) {
17
    return;
18
}
19
20
use Alcaeus\MongoDbAdapter\Helper;
21
use Alcaeus\MongoDbAdapter\ExceptionConverter;
22
use MongoDB\Client;
23
24
/**
25
 * A connection between PHP and MongoDB. This class is used to create and manage connections
26
 * See MongoClient::__construct() and the section on connecting for more information about creating connections.
27
 * @link http://www.php.net/manual/en/class.mongoclient.php
28
 */
29
class MongoClient
30
{
31
    use Helper\ReadPreference;
32
    use Helper\WriteConcern;
33
34
    const VERSION = '1.6.12';
35
    const DEFAULT_HOST = "localhost" ;
36
    const DEFAULT_PORT = 27017 ;
37
    const RP_PRIMARY = "primary" ;
38
    const RP_PRIMARY_PREFERRED = "primaryPreferred" ;
39
    const RP_SECONDARY = "secondary" ;
40
    const RP_SECONDARY_PREFERRED = "secondaryPreferred" ;
41
    const RP_NEAREST = "nearest" ;
42
43
    /**
44
     * @var bool
45
     * @deprecated This will not properly work as the underlying driver connects lazily
46
     */
47
    public $connected = false;
48
49
    /**
50
     * @var
51
     */
52
    public $status;
53
54
    /**
55
     * @var string
56
     */
57
    protected $server;
58
59
    /**
60
     * @var
61
     */
62
    protected $persistent;
63
64
    /**
65
     * @var Client
66
     */
67
    private $client;
68
69
    /**
70
     * @var \MongoDB\Driver\Manager
71
     */
72
    private $manager;
73
74
    /**
75
     * Creates a new database connection object
76
     *
77
     * @link http://php.net/manual/en/mongo.construct.php
78
     * @param string $server The server name.
79
     * @param array $options An array of options for the connection.
80
     * @param array $driverOptions An array of options for the MongoDB driver.
81
     * @throws MongoConnectionException
82
     */
83
    public function __construct($server = 'default', array $options = ['connect' => true], array $driverOptions = [])
84
    {
85
        if ($server === 'default') {
86
            $server = 'mongodb://' . self::DEFAULT_HOST . ':' . self::DEFAULT_PORT;
87
        }
88
89
        $this->applyConnectionOptions($server, $options);
90
91
        $this->server = $server;
92
        if (false === strpos($this->server, 'mongodb://')) {
93
            $this->server = 'mongodb://'.$this->server;
94
        }
95
        $this->client = new Client($this->server, $options, $driverOptions);
96
        $info = $this->client->__debugInfo();
97
        $this->manager = $info['manager'];
98
99
        if (isset($options['connect']) && $options['connect']) {
100
            $this->connect();
101
        }
102
    }
103
104
105
    /**
106
     * Closes this database connection
107
     *
108
     * @link http://www.php.net/manual/en/mongoclient.close.php
109
     * @param  boolean|string $connection
110
     * @return boolean If the connection was successfully closed.
111
     */
112
    public function close($connection = null)
0 ignored issues
show
Unused Code introduced by
The parameter $connection 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...
113
    {
114
        $this->connected = false;
1 ignored issue
show
Deprecated Code introduced by
The property MongoClient::$connected has been deprecated with message: This will not properly work as the underlying driver connects lazily

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
115
116
        return false;
117
    }
118
119
    /**
120
     * Connects to a database server
121
     *
122
     * @link http://www.php.net/manual/en/mongoclient.connect.php
123
     *
124
     * @throws MongoConnectionException
125
     * @return boolean If the connection was successful.
126
     */
127
    public function connect()
128
    {
129
        $this->connected = true;
1 ignored issue
show
Deprecated Code introduced by
The property MongoClient::$connected has been deprecated with message: This will not properly work as the underlying driver connects lazily

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
130
131
        return true;
132
    }
133
134
    /**
135
     * Drops a database
136
     *
137
     * @link http://www.php.net/manual/en/mongoclient.dropdb.php
138
     * @param mixed $db The database to drop. Can be a MongoDB object or the name of the database.
139
     * @return array The database response.
140
     * @deprecated Use MongoDB::drop() instead.
141
     */
142
    public function dropDB($db)
143
    {
144
        return $this->selectDB($db)->drop();
145
    }
146
147
    /**
148
     * Gets a database
149
     *
150
     * @link http://php.net/manual/en/mongoclient.get.php
151
     * @param string $dbname The database name.
152
     * @return MongoDB The database name.
153
     */
154
    public function __get($dbname)
155
    {
156
        return $this->selectDB($dbname);
157
    }
158
159
    /**
160
     * Gets the client for this object
161
     *
162
     * @internal This part is not of the ext-mongo API and should not be used
163
     * @return Client
164
     */
165
    public function getClient()
166
    {
167
        return $this->client;
168
    }
169
170
    /**
171
     * Get connections
172
     *
173
     * Returns an array of all open connections, and information about each of the servers
174
     *
175
     * @return array
176
     */
177
    public static function getConnections()
178
    {
179
        return [];
180
    }
181
182
    /**
183
     * Get hosts
184
     *
185
     * This method is only useful with a connection to a replica set. It returns the status of all of the hosts in the
186
     * set. Without a replica set, it will just return an array with one element containing the host that you are
187
     * connected to.
188
     *
189
     * @return array
190
     */
191
    public function getHosts()
192
    {
193
        $this->forceConnect();
194
195
        $results = [];
196
197
        try {
198
            $servers = $this->manager->getServers();
199
        } catch (\MongoDB\Driver\Exception\Exception $e) {
200
            throw ExceptionConverter::toLegacy($e);
201
        }
202
203
        foreach ($servers as $server) {
204
            $key = sprintf('%s:%d;-;.;%d', $server->getHost(), $server->getPort(), getmypid());
205
            $info = $server->getInfo();
206
207
            switch ($server->getType()) {
208
                case \MongoDB\Driver\Server::TYPE_RS_PRIMARY:
209
                    $state = 1;
210
                    break;
211
                case \MongoDB\Driver\Server::TYPE_RS_SECONDARY:
212
                    $state = 2;
213
                    break;
214
                default:
215
                    $state = 0;
216
            }
217
218
            $results[$key] = [
219
                'host' => $server->getHost(),
220
                'port' => $server->getPort(),
221
                'health' => (int) $info['ok'],
222
                'state' => $state,
223
                'ping' => $server->getLatency(),
224
                'lastPing' => null,
225
            ];
226
        }
227
228
        return $results;
229
    }
230
231
    /**
232
     * Kills a specific cursor on the server
233
     *
234
     * @link http://www.php.net/manual/en/mongoclient.killcursor.php
235
     * @param string $server_hash The server hash that has the cursor.
236
     * @param int|MongoInt64 $id The ID of the cursor to kill.
237
     * @return bool
238
     */
239
    public function killCursor($server_hash , $id)
0 ignored issues
show
Unused Code introduced by
The parameter $server_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...
Unused Code introduced by
The parameter $id 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...
240
    {
241
        $this->notImplemented();
242
    }
243
244
    /**
245
     * Lists all of the databases available
246
     *
247
     * @link http://php.net/manual/en/mongoclient.listdbs.php
248
     * @return array Returns an associative array containing three fields. The first field is databases, which in turn contains an array. Each element of the array is an associative array corresponding to a database, giving the database's name, size, and if it's empty. The other two fields are totalSize (in bytes) and ok, which is 1 if this method ran successfully.
249
     */
250
    public function listDBs()
251
    {
252
        try {
253
            $databaseInfoIterator = $this->client->listDatabases();
254
        } catch (\MongoDB\Driver\Exception\Exception $e) {
255
            throw ExceptionConverter::toLegacy($e);
256
        }
257
258
        $databases = [
259
            'databases' => [],
260
            'totalSize' => 0,
261
            'ok' => 1.0,
262
        ];
263
264
        foreach ($databaseInfoIterator as $databaseInfo) {
265
            $databases['databases'][] = [
266
                'name' => $databaseInfo->getName(),
267
                'empty' => $databaseInfo->isEmpty(),
268
                'sizeOnDisk' => $databaseInfo->getSizeOnDisk(),
269
            ];
270
            $databases['totalSize'] += $databaseInfo->getSizeOnDisk();
271
        }
272
273
        return $databases;
274
    }
275
276
    /**
277
     * Gets a database collection
278
     *
279
     * @link http://www.php.net/manual/en/mongoclient.selectcollection.php
280
     * @param string $db The database name.
281
     * @param string $collection The collection name.
282
     * @return MongoCollection Returns a new collection object.
283
     * @throws Exception Throws Exception if the database or collection name is invalid.
284
     */
285
    public function selectCollection($db, $collection)
286
    {
287
        return new MongoCollection($this->selectDB($db), $collection);
288
    }
289
290
    /**
291
     * Gets a database
292
     *
293
     * @link http://www.php.net/manual/en/mongo.selectdb.php
294
     * @param string $name The database name.
295
     * @return MongoDB Returns a new db object.
296
     * @throws InvalidArgumentException
297
     */
298
    public function selectDB($name)
299
    {
300
        return new MongoDB($this, $name);
301
    }
302
303
    /**
304
     * {@inheritdoc}
305
     */
306
    public function setReadPreference($readPreference, $tags = null)
307
    {
308
        return $this->setReadPreferenceFromParameters($readPreference, $tags);
309
    }
310
311
    /**
312
     * {@inheritdoc}
313
     */
314
    public function setWriteConcern($wstring, $wtimeout = 0)
315
    {
316
        return $this->setWriteConcernFromParameters($wstring, $wtimeout);
317
    }
318
319
    /**
320
     * String representation of this connection
321
     *
322
     * @link http://www.php.net/manual/en/mongoclient.tostring.php
323
     * @return string Returns hostname and port for this connection.
324
     */
325
    public function __toString()
326
    {
327
        return $this->server;
328
    }
329
330
    /**
331
     * Forces a connection by executing the ping command
332
     */
333
    private function forceConnect()
334
    {
335
        $command = new \MongoDB\Driver\Command(['ping' => 1]);
336
        $this->manager->executeCommand('db', $command);
337
    }
338
339
    private function notImplemented()
340
    {
341
        throw new \Exception('Not implemented');
342
    }
343
344
    /**
345
     * @return array
346
     */
347
    function __sleep()
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
348
    {
349
        return [
350
            'connected', 'status', 'server', 'persistent'
351
        ];
352
    }
353
354
    /**
355
     * @param $server
356
     * @return array
357
     */
358
    private function extractUrlOptions($server)
359
    {
360
        $queryOptions = explode('&', parse_url($server, PHP_URL_QUERY));
361
362
        $options = [];
363
        foreach ($queryOptions as $option) {
364
            if (strpos($option, '=') === false) {
365
                continue;
366
            }
367
368
            $keyValue = explode('=', $option);
369
            if ($keyValue[0] === 'readPreferenceTags') {
370
                $options[$keyValue[0]][] = $this->getReadPreferenceTags($keyValue[1]);
371
            } else {
372
                $options[$keyValue[0]] = $keyValue[1];
373
            }
374
        }
375
376
        return $options;
377
    }
378
379
    /**
380
     * @param $readPreferenceTagString
381
     * @return array
382
     */
383
    private function getReadPreferenceTags($readPreferenceTagString)
384
    {
385
        $tagSets = [];
386
        foreach (explode(',', $readPreferenceTagString) as $index => $tagSet) {
387
            $tags = explode(':', $tagSet);
388
            $tagSets[$tags[0]] = $tags[1];
389
        }
390
391
        return $tagSets;
392
    }
393
394
    /**
395
     * @param string $server
396
     * @param array $options
397
     */
398
    private function applyConnectionOptions($server, array $options)
399
    {
400
        $urlOptions = $this->extractUrlOptions($server);
401
402
        if (isset($urlOptions['wTimeout'])) {
403
            $urlOptions['wTimeoutMS'] = $urlOptions['wTimeout'];
404
            unset($urlOptions['wTimeout']);
405
        }
406
407 View Code Duplication
        if (isset($options['wTimeout'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
408
            $options['wTimeoutMS'] = $options['wTimeout'];
409
            unset($options['wTimeout']);
410
        }
411
412
        if (isset($options['readPreferenceTags'])) {
413
            $options['readPreferenceTags'] = [$this->getReadPreferenceTags($options['readPreferenceTags'])];
414
415
            // Special handling for readPreferenceTags which are merged
416
            if (isset($urlOptions['readPreferenceTags'])) {
417
                $options['readPreferenceTags'] = array_merge($urlOptions['readPreferenceTags'], $options['readPreferenceTags']);
418
            }
419
        }
420
421
        $urlOptions = array_merge($urlOptions, $options);
422
423
        if (isset($urlOptions['slaveOkay'])) {
424
            $this->setReadPreferenceFromSlaveOkay($urlOptions['slaveOkay']);
425
        } elseif (isset($urlOptions['readPreference']) || isset($urlOptions['readPreferenceTags'])) {
426
            $readPreference = isset($urlOptions['readPreference']) ? $urlOptions['readPreference'] : null;
427
            $tags = isset($urlOptions['readPreferenceTags']) ? $urlOptions['readPreferenceTags'] : null;
428
            $this->setReadPreferenceFromParameters($readPreference, $tags);
429
        }
430
431
        if (isset($urlOptions['w']) || isset($urlOptions['wTimeoutMs'])) {
432
            $writeConcern = (isset($urlOptions['w'])) ? $urlOptions['w'] : 1;
433
            $wTimeout = (isset($urlOptions['wTimeoutMs'])) ? $urlOptions['wTimeoutMs'] : null;
434
            $this->setWriteConcern($writeConcern, $wTimeout);
435
        }
436
    }
437
}
438