Completed
Push — master ( 660564...17f31f )
by Andreas
18s queued 10s
created

MongoClient::killCursor()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 2
dl 0
loc 4
rs 10
c 0
b 0
f 0
1
<?php
2
/*
3
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
4
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
5
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
6
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
7
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
8
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
9
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
10
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
11
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
12
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
13
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
14
 */
15
16
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
        if (isset($options['readPreferenceTags'])) {
90
            $options['readPreferenceTags'] = [$this->getReadPreferenceTags($options['readPreferenceTags'])];
91
        }
92
93
        $this->applyConnectionOptions($server, $options);
94
95
        $this->server = $server;
96
        if (false === strpos($this->server, '://')) {
97
            $this->server = 'mongodb://' . $this->server;
98
        }
99
        $this->client = new Client($this->server, $options, $driverOptions);
100
        $info = $this->client->__debugInfo();
101
        $this->manager = $info['manager'];
102
103
        if (isset($options['connect']) && $options['connect']) {
104
            $this->connect();
105
        }
106
    }
107
108
109
    /**
110
     * Closes this database connection
111
     *
112
     * @link http://www.php.net/manual/en/mongoclient.close.php
113
     * @param  boolean|string $connection
114
     * @return boolean If the connection was successfully closed.
115
     */
116
    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...
117
    {
118
        $this->connected = false;
119
120
        return false;
121
    }
122
123
    /**
124
     * Connects to a database server
125
     *
126
     * @link http://www.php.net/manual/en/mongoclient.connect.php
127
     *
128
     * @throws MongoConnectionException
129
     * @return boolean If the connection was successful.
130
     */
131
    public function connect()
132
    {
133
        $this->connected = true;
0 ignored issues
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...
134
135
        return true;
136
    }
137
138
    /**
139
     * Drops a database
140
     *
141
     * @link http://www.php.net/manual/en/mongoclient.dropdb.php
142
     * @param mixed $db The database to drop. Can be a MongoDB object or the name of the database.
143
     * @return array The database response.
144
     * @deprecated Use MongoDB::drop() instead.
145
     */
146
    public function dropDB($db)
147
    {
148
        return $this->selectDB($db)->drop();
149
    }
150
151
    /**
152
     * Gets a database
153
     *
154
     * @link http://php.net/manual/en/mongoclient.get.php
155
     * @param string $dbname The database name.
156
     * @return MongoDB The database name.
157
     */
158
    public function __get($dbname)
159
    {
160
        return $this->selectDB($dbname);
161
    }
162
163
    /**
164
     * Gets the client for this object
165
     *
166
     * @internal This part is not of the ext-mongo API and should not be used
167
     * @return Client
168
     */
169
    public function getClient()
170
    {
171
        return $this->client;
172
    }
173
174
    /**
175
     * Get connections
176
     *
177
     * Returns an array of all open connections, and information about each of the servers
178
     *
179
     * @return array
180
     */
181
    public static function getConnections()
182
    {
183
        return [];
184
    }
185
186
    /**
187
     * Get hosts
188
     *
189
     * This method is only useful with a connection to a replica set. It returns the status of all of the hosts in the
190
     * set. Without a replica set, it will just return an array with one element containing the host that you are
191
     * connected to.
192
     *
193
     * @return array
194
     */
195
    public function getHosts()
196
    {
197
        $this->forceConnect();
198
199
        $results = [];
200
201
        try {
202
            $servers = $this->manager->getServers();
203
        } catch (\MongoDB\Driver\Exception\Exception $e) {
204
            throw ExceptionConverter::toLegacy($e);
205
        }
206
207
        foreach ($servers as $server) {
208
            $key = sprintf('%s:%d;-;.;%d', $server->getHost(), $server->getPort(), getmypid());
209
            $info = $server->getInfo();
0 ignored issues
show
Unused Code introduced by
$info is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
210
211
            switch ($server->getType()) {
212
                case \MongoDB\Driver\Server::TYPE_RS_PRIMARY:
213
                    $state = 1;
214
                    break;
215
                case \MongoDB\Driver\Server::TYPE_RS_SECONDARY:
216
                    $state = 2;
217
                    break;
218
                default:
219
                    $state = 0;
220
            }
221
222
            $results[$key] = [
223
                'host' => $server->getHost(),
224
                'port' => $server->getPort(),
225
                'health' => 1,
226
                'state' => $state,
227
                'ping' => $server->getLatency(),
228
                'lastPing' => null,
229
            ];
230
        }
231
232
        return $results;
233
    }
234
235
    /**
236
     * Kills a specific cursor on the server
237
     *
238
     * @link http://www.php.net/manual/en/mongoclient.killcursor.php
239
     * @param string $server_hash The server hash that has the cursor.
240
     * @param int|MongoInt64 $id The ID of the cursor to kill.
241
     * @return bool
242
     */
243
    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...
244
    {
245
        $this->notImplemented();
246
    }
247
248
    /**
249
     * Lists all of the databases available
250
     *
251
     * @link http://php.net/manual/en/mongoclient.listdbs.php
252
     * @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.
253
     */
254
    public function listDBs()
255
    {
256
        try {
257
            $databaseInfoIterator = $this->client->listDatabases();
258
        } catch (\MongoDB\Driver\Exception\Exception $e) {
259
            throw ExceptionConverter::toLegacy($e);
260
        }
261
262
        $databases = [
263
            'databases' => [],
264
            'totalSize' => 0,
265
            'ok' => 1.0,
266
        ];
267
268
        foreach ($databaseInfoIterator as $databaseInfo) {
269
            $databases['databases'][] = [
270
                'name' => $databaseInfo->getName(),
271
                'empty' => $databaseInfo->isEmpty(),
272
                'sizeOnDisk' => $databaseInfo->getSizeOnDisk(),
273
            ];
274
            $databases['totalSize'] += $databaseInfo->getSizeOnDisk();
275
        }
276
277
        return $databases;
278
    }
279
280
    /**
281
     * Gets a database collection
282
     *
283
     * @link http://www.php.net/manual/en/mongoclient.selectcollection.php
284
     * @param string $db The database name.
285
     * @param string $collection The collection name.
286
     * @return MongoCollection Returns a new collection object.
287
     * @throws Exception Throws Exception if the database or collection name is invalid.
288
     */
289
    public function selectCollection($db, $collection)
290
    {
291
        return new MongoCollection($this->selectDB($db), $collection);
292
    }
293
294
    /**
295
     * Gets a database
296
     *
297
     * @link http://www.php.net/manual/en/mongo.selectdb.php
298
     * @param string $name The database name.
299
     * @return MongoDB Returns a new db object.
300
     * @throws InvalidArgumentException
301
     */
302
    public function selectDB($name)
303
    {
304
        return new MongoDB($this, $name);
305
    }
306
307
    /**
308
     * {@inheritdoc}
309
     */
310
    public function setReadPreference($readPreference, $tags = null)
311
    {
312
        return $this->setReadPreferenceFromParameters($readPreference, $tags);
313
    }
314
315
    /**
316
     * {@inheritdoc}
317
     */
318
    public function setWriteConcern($wstring, $wtimeout = 0)
319
    {
320
        return $this->setWriteConcernFromParameters($wstring, $wtimeout);
321
    }
322
323
    /**
324
     * String representation of this connection
325
     *
326
     * @link http://www.php.net/manual/en/mongoclient.tostring.php
327
     * @return string Returns hostname and port for this connection.
328
     */
329
    public function __toString()
330
    {
331
        return $this->server;
332
    }
333
334
    /**
335
     * Forces a connection by executing the ping command
336
     */
337
    private function forceConnect()
338
    {
339
        try {
340
            $command = new \MongoDB\Driver\Command(['ping' => 1]);
341
            $this->manager->executeCommand('db', $command);
342
        } catch (\MongoDB\Driver\Exception\Exception $e) {
343
            throw ExceptionConverter::toLegacy($e);
344
        }
345
    }
346
347
    private function notImplemented()
348
    {
349
        throw new \Exception('Not implemented');
350
    }
351
352
    /**
353
     * @return array
354
     */
355
    public function __sleep()
356
    {
357
        return [
358
            'connected', 'status', 'server', 'persistent'
359
        ];
360
    }
361
362
    /**
363
     * @param $server
364
     * @return array
365
     */
366
    private function extractUrlOptions($server)
367
    {
368
        $queryOptions = explode('&', parse_url($server, PHP_URL_QUERY));
369
370
        $options = [];
371
        foreach ($queryOptions as $option) {
372
            if (strpos($option, '=') === false) {
373
                continue;
374
            }
375
376
            $keyValue = explode('=', $option);
377
            if ($keyValue[0] === 'readPreferenceTags') {
378
                $options[$keyValue[0]][] = $this->getReadPreferenceTags($keyValue[1]);
379
            } elseif (ctype_digit($keyValue[1])) {
380
                $options[$keyValue[0]] = (int) $keyValue[1];
381
            } else {
382
                $options[$keyValue[0]] = $keyValue[1];
383
            }
384
        }
385
386
        return $options;
387
    }
388
389
    /**
390
     * @param $readPreferenceTagString
391
     * @return array
392
     */
393
    private function getReadPreferenceTags($readPreferenceTagString)
394
    {
395
        $tagSets = [];
396
        foreach (explode(',', $readPreferenceTagString) as $index => $tagSet) {
397
            $tags = explode(':', $tagSet);
398
            $tagSets[$tags[0]] = $tags[1];
399
        }
400
401
        return $tagSets;
402
    }
403
404
    /**
405
     * @param string $server
406
     * @param array $options
407
     */
408
    private function applyConnectionOptions($server, array $options)
409
    {
410
        $urlOptions = $this->extractUrlOptions($server);
411
412
        if (isset($urlOptions['wTimeout'])) {
413
            $urlOptions['wTimeoutMS'] = $urlOptions['wTimeout'];
414
            unset($urlOptions['wTimeout']);
415
        }
416
417 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...
418
            $options['wTimeoutMS'] = $options['wTimeout'];
419
            unset($options['wTimeout']);
420
        }
421
422
        // Special handling for readPreferenceTags which are merged
423
        if (isset($options['readPreferenceTags']) && isset($urlOptions['readPreferenceTags'])) {
424
            $options['readPreferenceTags'] = array_merge($urlOptions['readPreferenceTags'], $options['readPreferenceTags']);
425
            unset($urlOptions['readPreferenceTags']);
426
        }
427
428
        $urlOptions = array_merge($urlOptions, $options);
429
430
        if (isset($urlOptions['slaveOkay'])) {
431
            $this->setReadPreferenceFromSlaveOkay($urlOptions['slaveOkay']);
432
        } elseif (isset($urlOptions['readPreference']) || isset($urlOptions['readPreferenceTags'])) {
433
            $readPreference = isset($urlOptions['readPreference']) ? $urlOptions['readPreference'] : null;
434
            $tags = isset($urlOptions['readPreferenceTags']) ? $urlOptions['readPreferenceTags'] : null;
435
            $this->setReadPreferenceFromParameters($readPreference, $tags);
436
        }
437
438
        if (isset($urlOptions['w']) || isset($urlOptions['wTimeoutMs'])) {
439
            $writeConcern = (isset($urlOptions['w'])) ? $urlOptions['w'] : 1;
440
            $wTimeout = (isset($urlOptions['wTimeoutMs'])) ? $urlOptions['wTimeoutMs'] : null;
441
            $this->setWriteConcern($writeConcern, $wTimeout);
442
        }
443
    }
444
}
445