Completed
Pull Request — master (#243)
by Дмитрий
05:05
created

Pool::saslScrumSHA1Auth()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 13
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 1 Features 0
Metric Value
cc 2
eloc 10
c 1
b 1
f 0
nc 2
nop 2
dl 0
loc 13
rs 9.4285
1
<?php
2
namespace PHPDaemon\Clients\Mongo;
3
4
use PHPDaemon\Clients\Mongo\Collection;
5
use PHPDaemon\Clients\Mongo\Connection;
6
use PHPDaemon\Clients\Mongo\ConnectionFinished;
7
use PHPDaemon\Network\Client;
8
use PHPDaemon\Core\CallbackWrapper;
9
use PHPDaemon\Core\Daemon;
10
use PHPDaemon\Core\Debug;
11
12
/**
13
 * @package    Applications
14
 * @subpackage MongoClientAsync
15
 * @author     Vasily Zorin <[email protected]>
16
 */
17
class Pool extends Client
18
{
19
    use \PHPDaemon\Traits\StaticObjectWatchdog;
20
21
    /* Codes of operations */
22
23
    /**
24
     * @TODO DESCR
0 ignored issues
show
Coding Style introduced by
Comment refers to a TODO task

This check looks TODO comments that have been left in the code.

``TODO``s show that something is left unfinished and should be attended to.

Loading history...
25
     */
26
    const OP_REPLY = 1;
27
28
    /**
29
     * @TODO DESCR
0 ignored issues
show
Coding Style introduced by
Comment refers to a TODO task

This check looks TODO comments that have been left in the code.

``TODO``s show that something is left unfinished and should be attended to.

Loading history...
30
     */
31
    const OP_MSG = 1000;
32
33
    /**
34
     * @TODO DESCR
0 ignored issues
show
Coding Style introduced by
Comment refers to a TODO task

This check looks TODO comments that have been left in the code.

``TODO``s show that something is left unfinished and should be attended to.

Loading history...
35
     */
36
    const OP_UPDATE = 2001;
37
38
    /**
39
     * @TODO DESCR
0 ignored issues
show
Coding Style introduced by
Comment refers to a TODO task

This check looks TODO comments that have been left in the code.

``TODO``s show that something is left unfinished and should be attended to.

Loading history...
40
     */
41
    const OP_INSERT = 2002;
42
43
    /**
44
     * @TODO DESCR
0 ignored issues
show
Coding Style introduced by
Comment refers to a TODO task

This check looks TODO comments that have been left in the code.

``TODO``s show that something is left unfinished and should be attended to.

Loading history...
45
     */
46
    const OP_QUERY = 2004;
47
48
    /**
49
     * @TODO DESCR
0 ignored issues
show
Coding Style introduced by
Comment refers to a TODO task

This check looks TODO comments that have been left in the code.

``TODO``s show that something is left unfinished and should be attended to.

Loading history...
50
     */
51
    const OP_GETMORE = 2005;
52
53
    /**
54
     * @TODO DESCR
0 ignored issues
show
Coding Style introduced by
Comment refers to a TODO task

This check looks TODO comments that have been left in the code.

``TODO``s show that something is left unfinished and should be attended to.

Loading history...
55
     */
56
    const OP_DELETE = 2006;
57
58
    /**
59
     * @TODO DESCR
0 ignored issues
show
Coding Style introduced by
Comment refers to a TODO task

This check looks TODO comments that have been left in the code.

``TODO``s show that something is left unfinished and should be attended to.

Loading history...
60
     */
61
    const OP_KILL_CURSORS = 2007;
62
63
    /**
64
     * @var array Objects of MongoClientAsyncCollection
65
     */
66
    public $collections = [];
67
68
    /**
69
     * @var string Current database
70
     */
71
    public $dbname = '';
72
73
    /**
74
     * @var Connection Holds last used MongoClientAsyncConnection object
75
     */
76
    public $lastRequestConnection;
77
78
    /**
79
     * @var object Object of MemcacheClient
80
     */
81
    public $cache;
82
83
    protected $safeMode = true;
84
85
    /**
86
     * Setting default config options
87
     * Overriden from AppInstance::getConfigDefaults
88
     * @return array|bool
89
     */
90
    protected function getConfigDefaults()
91
    {
92
        return [
93
            /* [string|array] default server list */
0 ignored issues
show
Unused Code Comprehensibility introduced by
50% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
94
            'servers' => 'tcp://127.0.0.1',
95
96
            /* [integer] default port */
0 ignored issues
show
Unused Code Comprehensibility introduced by
38% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
97
            'port' => 27017,
98
99
            /* [integer] maxconnperserv */
100
            'maxconnperserv' => 32,
101
        ];
102
    }
103
104
    /**
105
     * @TODO
0 ignored issues
show
Coding Style introduced by
Comment refers to a TODO task

This check looks TODO comments that have been left in the code.

``TODO``s show that something is left unfinished and should be attended to.

Loading history...
106
     * @param  array $o
107
     * @return void
108
     */
109
    public static function safeModeEnc(&$o)
110
    {
111
        foreach ($o as &$v) {
112
            if (is_array($v)) {
113
                static::safeModeEnc($v);
114
            } elseif ($v instanceof MongoId) {
115
                $v = $v->getPlainObject();
116
            }
117
        }
118
    }
119
120
    /**
121
     * Sets default database name
122
     * @param  string $name Database name
123
     * @return boolean       Success
124
     */
125
    public function selectDB($name)
126
    {
127
        $this->dbname = $name;
128
129
        return true;
130
    }
131
132
    /**
133
     * Generates auth. key
134
     * @param  string $username Username
135
     * @param  string $password Password
136
     * @param  string $nonce Nonce
137
     * @return string           MD5 hash
138
     */
139
    public static function getAuthKey($username, $password, $nonce)
140
    {
141
        return md5($nonce . $username . md5($username . ':mongo:' . $password));
142
    }
143
144
    /**
145
     * Adds mongo server
146
     * @param string $url URL
147
     * @param integer $weight Weight
0 ignored issues
show
Documentation introduced by
Should the type for parameter $weight not be integer|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
148
     * @param mixed $mock @deprecated
149
     * @return void
150
     */
151
    public function addServer($url, $weight = null, $mock = null)
0 ignored issues
show
Unused Code introduced by
The parameter $mock 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...
152
    {
153
        $this->servers[$url] = $weight;
154
    }
155
156
    /**
157
     * Gets the key
158
     * @param  integer $opcode Opcode (see constants above)
159
     * @param  string $data Data
160
     * @param  boolean $reply Is an answer expected?
161
     * @param  Connection $conn Connection. Optional
0 ignored issues
show
Documentation introduced by
Should the type for parameter $conn not be Connection|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
162
     * @param  callable $sentcb Sent callback
0 ignored issues
show
Documentation introduced by
Should the type for parameter $sentcb not be callable|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
163
     * @callback $sentcb ( )
164
     * @throws ConnectionFinished
165
     * @return void
166
     */
167
    public function request($opcode, $data, $reply = false, $conn = null, $sentcb = null)
168
    {
169
        $cb = $this->requestCbProducer($opcode, $data, $reply, $sentcb);
170
        if (is_object($conn) && ($conn instanceof Connection)) {
171
            if ($conn->isFinished()) {
172
                throw new ConnectionFinished;
173
            }
174
            $cb($conn);
175
        } elseif ($this->finished) {
176
            $cb(false);
177
        } else {
178
            $this->getConnectionRR($cb);
179
        }
180
    }
181
182
    /**
183
     * @TODO DESCR
0 ignored issues
show
Coding Style introduced by
Comment refers to a TODO task

This check looks TODO comments that have been left in the code.

``TODO``s show that something is left unfinished and should be attended to.

Loading history...
184
     * @param  integer $opcode Opcode (see constants above)
185
     * @param  string $data Data
186
     * @param  boolean $reply Is an answer expected?
187
     * @param  callable $sentcb Sent callback
0 ignored issues
show
Documentation introduced by
Should the type for parameter $sentcb not be callable|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
188
     * @callback $sentcb ( )
189
     * @return callable
190
     */
191
    protected function requestCbProducer($opcode, $data, $reply = false, $sentcb = null)
192
    {
193
        return function ($conn) use ($opcode, $data, $reply, $sentcb) {
194
            if (!$conn || $conn->isFinished()) {
195
                if ($this->finished) {
196
                    if ($sentcb !== null) {
197
                        $sentcb(false);
198
                    }
199
                } else {
200
                    $this->getConnectionRR($this->requestCbProducer($opcode, $data, $reply, $sentcb));
201
                }
202
                return;
203
            }
204
            $reqId = ++$conn->lastReqId;
205
            $this->lastRequestConnection = $conn;
206
            $conn->write(pack('VVVV', mb_orig_strlen($data) + 16, $reqId, 0, $opcode));
207
            $conn->write($data);
208
            if ($reply) {
209
                $conn->setFree(false);
210
            }
211
            if ($sentcb !== null) {
212
                $sentcb($conn, $reqId);
213
            }
214
        };
215
    }
216
217
    /**
218
     * Finds objects in collection and fires callback when got all objects
219
     * @param  array $p Hash of properties (offset, limit, opts, tailable, await, where, col, fields, sort, hint, explain, snapshot, orderby, parse_oplog)
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 154 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
220
     * @param  callable $cb Callback called when response received
221
     * @callback $cb ( )
222
     * @return void
223
     */
224
    public function findAll($p, $cb)
225
    {
226
        $this->find($p, function ($cursor) use ($cb) {
227
            if (!$cursor->isFinished()) {
228
                $cursor->getMore();
229
            } else {
230
                $cb($cursor);
231
            }
232
        });
233
    }
234
235
    /**
236
     * Finds objects in collection
237
     * @param  array $p Hash of properties (offset, limit, opts, tailable, await, where, col, fields, sort, hint, explain, snapshot, orderby, parse_oplog)
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 154 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
238
     * @param  callable $cb Callback called when response received
239
     * @callback $cb ( )
240
     * @return void
241
     */
242
    public function find($p, $cb)
243
    {
244
        if (!isset($p['offset'])) {
245
            $p['offset'] = 0;
246
        }
247
248
        if (!isset($p['limit'])) {
249
            $p['limit'] = 0;
250
        }
251
252
        if (!isset($p['opts'])) {
253
            $p['opts'] = '0';
254
        }
255
256
        if (isset($p['tailable']) && $p['tailable']) {
257
            $p['opts'] = '01000' . (isset($p['await']) && $p['await'] ? '1' : '0') . '00';
258
        }
259
260
        if (!isset($p['where'])) {
261
            $p['where'] = [];
262
        }
263
264
        $this->_params($p);
265
266
        $o = [];
267
        $s = false;
268
269 View Code Duplication
        foreach ($p as $k => $v) {
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...
270
            if (($k === 'sort') || ($k === 'hint') || ($k === 'explain') || ($k === 'snapshot')) {
271
                if (!$s) {
272
                    $s = true;
273
                }
274
275
                if ($k === 'sort') {
276
                    $o['orderby'] = $v;
277
                } elseif ($k === 'parse_oplog') {
0 ignored issues
show
Unused Code introduced by
This elseif statement is empty, and could be removed.

This check looks for the bodies of elseif statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

These elseif bodies can be removed. If you have an empty elseif but statements in the else branch, consider inverting the condition.

Loading history...
278
                } elseif ($k === 'rp') {
279
                    $o['$readPreference'] = $v;
280
                } else {
281
                    $o[$k] = $v;
282
                }
283
            }
284
        }
285
286
        if ($s) {
287
            $o['query'] = $p['where'];
288
        } else {
289
            $o = $p['where'];
290
        }
291
292
        if (empty($o['orderby'])) {
293
            unset($o['orderby']);
294
        }
295
296
        if ($this->safeMode) {
297
            static::safeModeEnc($o);
298
        }
299
        try {
300
            $bson = bson_encode($o);
301
302
            if (isset($p['parse_oplog'])) {
303
                $bson = str_replace("\x11\$gt", "\x09\$gt", $bson);
304
            }
305
            $cb = CallbackWrapper::wrap($cb);
306
            $this->request(
307
                self::OP_QUERY,
308
                chr(bindec(strrev($p['opts']))) . "\x00\x00\x00" . $p['col'] . "\x00"
309
                . pack('VV', $p['offset'], $p['limit']) . $bson
310
                . (isset($p['fields']) ? bson_encode($p['fields']) : ''),
311
                true,
312
                null,
313
                function ($conn, $reqId = null) use ($p, $cb) {
314
                    if (!$conn) {
315
                        !$cb || $cb(['$err' => 'Connection error.']);
316
                        return;
317
                    }
318
                    $conn->requests[$reqId] = [$p['col'], $cb, false, isset($p['parse_oplog']), isset($p['tailable'])];
319
                }
320
            );
321
        } catch (\MongoException $e) {
322
            Daemon::log('MongoClient exception: ' . $e->getMessage() . ': ' . $e->getTraceAsString());
323 View Code Duplication
            if ($cb !== null) {
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...
324
                $cb([
325
                    '$err' => $e->getMessage(),
326
                    '$query' => $o,
327
                    '$fields' => isset($p['fields']) ? $p['fields'] : null
328
                ]);
329
            }
330
        }
331
    }
332
333
    /**
334
     * Finds one object in collection
335
     * @param  array $p Hash of properties (offset,  opts, where, col, fields, sort, hint, explain, snapshot, orderby, parse_oplog)
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 131 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
336
     * @param  callable $cb Callback called when response received
337
     * @callback $cb ( )
338
     * @return void
339
     */
340
    public function findOne($p, $cb)
341
    {
342
        if (isset($p['cachekey'])) {
343
            $db = $this;
344
            $this->cache->get($p['cachekey'], function ($r) use ($db, $p, $cb) {
345
                if ($r->result !== null) {
346
                    $cb(bson_decode($r->result));
347
                } else {
348
                    unset($p['cachekey']);
349
                    $db->findOne($p, $cb);
350
                }
351
            });
352
353
            return;
354
        }
355
        if (!isset($p['where'])) {
356
            $p['where'] = [];
357
        }
358
359
        $this->_params($p);
360
361
        $o = [];
362
        $s = false;
363
364 View Code Duplication
        foreach ($p as $k => $v) {
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...
365
            if (($k === 'sort') || ($k === 'hint') || ($k === 'explain') || ($k === 'snapshot')) {
366
                if (!$s) {
367
                    $s = true;
368
                }
369
370
                if ($k === 'sort') {
371
                    $o['orderby'] = $v;
372
                } elseif ($k === 'parse_oplog') {
0 ignored issues
show
Unused Code introduced by
This elseif statement is empty, and could be removed.

This check looks for the bodies of elseif statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

These elseif bodies can be removed. If you have an empty elseif but statements in the else branch, consider inverting the condition.

Loading history...
373
                } elseif ($k === 'rp') {
374
                    $o['$readPreference'] = $v;
375
                } else {
376
                    $o[$k] = $v;
377
                }
378
            }
379
        }
380
        if (empty($o['orderby'])) {
381
            unset($o['orderby']);
382
        }
383
384
        if ($s) {
385
            $o['query'] = $p['where'];
386
        } else {
387
            $o = $p['where'];
388
        }
389
        $cb = CallbackWrapper::wrap($cb);
390
        if ($this->safeMode) {
391
            static::safeModeEnc($o);
392
        }
393
        try {
394
            $this->request(
395
                self::OP_QUERY,
396
                pack('V', $p['opts']) . $p['col'] . "\x00" . pack('VV', $p['offset'], -1)
397
                . bson_encode($o) . (isset($p['fields']) ? bson_encode($p['fields']) : ''),
398
                true,
399
                null,
400 View Code Duplication
                function ($conn, $reqId = null) use ($p, $cb) {
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...
401
                    if (!$conn) {
402
                        !$cb || $cb(['$err' => 'Connection error.']);
403
                        return;
404
                    }
405
                    $conn->requests[$reqId] = [$p['col'], $cb, true];
406
                }
407
            );
408
        } catch (\MongoException $e) {
409
            Daemon::log('MongoClient exception: ' . $e->getMessage() . ': ' . $e->getTraceAsString());
410 View Code Duplication
            if ($cb !== null) {
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...
411
                $cb([
412
                    '$err' => $e->getMessage(),
413
                    '$query' => $o,
414
                    '$fields' => isset($p['fields']) ? $p['fields'] : null
415
                ]);
416
            }
417
        }
418
    }
419
420
    /**
421
     * Counts objects in collection
422
     * @param  array $p Hash of properties (offset, limit, opts, where, col)
423
     * @param  callable $cb Callback called when response received
424
     * @callback $cb ( )
425
     * @return void
426
     */
427
    public function findCount($p, $cb)
428
    {
429
        if (!isset($p['offset'])) {
430
            $p['offset'] = 0;
431
        }
432
433
        if (!isset($p['limit'])) {
434
            $p['limit'] = -1;
435
        }
436
437
        if (!isset($p['opts'])) {
438
            $p['opts'] = 0;
439
        }
440
441
        if (!isset($p['where'])) {
442
            $p['where'] = [];
443
        }
444
445 View Code Duplication
        if (mb_orig_strpos($p['col'], '.') === false) {
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...
446
            $p['col'] = $this->dbname . '.' . $p['col'];
447
        }
448
449
        $e = explode('.', $p['col'], 2);
450
451
        $query = [
452
            'count' => $e[1],
453
            'query' => $p['where'],
454
            'fields' => ['_id' => 1],
455
        ];
456
457 View Code Duplication
        if (isset($p[$k = 'rp'])) {
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...
458
            $v = $p[$k];
459
            if (is_string($v)) {
460
                $v = ['mode' => $v];
461
            }
462
            $query['$readPreference'] = $v;
463
        }
464
465 View Code Duplication
        if (is_string($p['where'])) {
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...
466
            $query['where'] = new \MongoCode($p['where']);
467
        } elseif (is_object($p['where']) || sizeof($p['where'])) {
468
            $query['query'] = $p['where'];
469
        }
470
        $cb = CallbackWrapper::wrap($cb);
471
        if ($this->safeMode) {
472
            static::safeModeEnc($query);
473
        }
474
        try {
475
            $this->request(
476
                self::OP_QUERY,
477
                pack('V', $p['opts']) . $e[0] . '.$cmd' . "\x00" . pack('VV', $p['offset'], $p['limit'])
478
                . bson_encode($query) . (isset($p['fields']) ? bson_encode($p['fields']) : ''),
479
                true,
480
                null,
481 View Code Duplication
                function ($conn, $reqId = null) use ($p, $cb) {
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...
482
                    if (!$conn) {
483
                        !$cb || $cb(['$err' => 'Connection error.']);
484
                        return;
485
                    }
486
                    $conn->requests[$reqId] = [$p['col'], $cb, true];
487
                }
488
            );
489
        } catch (\MongoException $e) {
490
            Daemon::log('MongoClient exception: ' . $e->getMessage() . ': ' . $e->getTraceAsString());
491 View Code Duplication
            if ($cb !== null) {
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...
492
                $cb([
493
                    '$err' => $e->getMessage(),
494
                    '$query' => $query,
495
                    '$fields' => isset($p['fields']) ? $p['fields'] : null
496
                ]);
497
            }
498
        }
499
    }
500
501
    /**
502
     * Sends authenciation packet
503
     * @param  array $p Hash of properties (dbname, user, password, nonce)
504
     * @param  callable $cb Callback called when response received
505
     * @callback $cb ( )
506
     * @return void
507
     */
508
    public function auth($p, $cb)
509
    {
510
        if (!isset($p['opts'])) {
511
            $p['opts'] = 0;
512
        }
513
514
        $query = [
515
            'authenticate' => 1,
516
            'user' => $p['user'],
517
            'nonce' => $p['nonce'],
518
            'key' => self::getAuthKey($p['user'], $p['password'], $p['nonce']),
519
        ];
520
        if ($this->safeMode) {
521
            static::safeModeEnc($query);
522
        }
523
        try {
524
            $this->request(
525
                self::OP_QUERY,
526
                pack('V', $p['opts']) . $p['dbname'] . '.$cmd' . "\x00" . pack('VV', 0, -1)
527
                . bson_encode($query) . (isset($p['fields']) ? bson_encode($p['fields']) : ''),
528
                true,
529
                null,
530 View Code Duplication
                function ($conn, $reqId = null) use ($p, $cb) {
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...
531
                    if (!$conn) {
532
                        !$cb || $cb(['$err' => 'Connection error.']);
533
                        return;
534
                    }
535
                    $conn->requests[$reqId] = [$p['dbname'], $cb, true];
536
                }
537
            );
538
        } catch (\MongoException $e) {
539
            Daemon::log('MongoClient exception: ' . $e->getMessage() . ': ' . $e->getTraceAsString());
540 View Code Duplication
            if ($cb !== null) {
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...
541
                $cb([
542
                    '$err' => $e->getMessage(),
543
                    '$query' => $query,
544
                    '$fields' => isset($p['fields']) ? $p['fields'] : null
545
                ]);
546
            }
547
        }
548
    }
549
550
    /**
551
     * Sends request of nonce
552
     * @param  array $p Hash of properties
553
     * @param  callable $cb Callback called when response received
554
     * @callback $cb ( )
555
     * @return void
556
     */
557
    public function getNonce($p, $cb)
558
    {
559
        if (!isset($p['opts'])) {
560
            $p['opts'] = 0;
561
        }
562
563
        $query = [
564
            'getnonce' => 1,
565
        ];
566
        $cb = CallbackWrapper::wrap($cb);
567
        try {
568
            $this->request(
569
                self::OP_QUERY,
570
                pack('V', $p['opts']) . $p['dbname'] . '.$cmd' . "\x00"
571
                . pack('VV', 0, -1) . bson_encode($query)
572
                . (isset($p['fields']) ? bson_encode($p['fields']) : ''),
573
                true,
574
                null,
575 View Code Duplication
                function ($conn, $reqId = null) use ($p, $cb) {
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...
576
                    if (!$conn) {
577
                        !$cb || $cb(['$err' => 'Connection error.']);
578
                        return;
579
                    }
580
                    $conn->requests[$reqId] = [$p['dbname'], $cb, true];
581
                }
582
            );
583
        } catch (\MongoException $e) {
584
            Daemon::log('MongoClient exception: ' . $e->getMessage() . ': ' . $e->getTraceAsString());
585 View Code Duplication
            if ($cb !== null) {
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...
586
                $cb([
587
                    '$err' => $e->getMessage(),
588
                    '$query' => $query,
589
                    '$fields' => isset($p['fields']) ? $p['fields'] : null
590
                ]);
591
            }
592
        }
593
    }
594
595
    /**
596
     * @TODO DESCR
0 ignored issues
show
Coding Style introduced by
Comment refers to a TODO task

This check looks TODO comments that have been left in the code.

``TODO``s show that something is left unfinished and should be attended to.

Loading history...
597
     * @param  array $keys
598
     * @return string
599
     */
600
    public function getIndexName($keys)
601
    {
602
        $name = '';
603
        $first = true;
604
        foreach ($keys as $k => $v) {
605
            $name .= ($first ? '_' : '') . $k . '_' . $v;
606
            $first = false;
607
        }
608
        return $name;
609
    }
610
611
    /**
612
     * Ensure index
613
     * @param  string $ns Collection
614
     * @param  array $keys Keys
615
     * @param  array $options Optional. Options
616
     * @param  callable $cb Optional. Callback called when response received
0 ignored issues
show
Documentation introduced by
Should the type for parameter $cb not be callable|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
617
     * @callback $cb ( )
618
     * @return void
619
     */
620
    public function ensureIndex($ns, $keys, $options = [], $cb = null)
621
    {
622
        $e = explode('.', $ns, 2);
623
        $doc = [
624
            'ns' => $ns,
625
            'key' => $keys,
626
            'name' => isset($options['name']) ? $options['name'] : $this->getIndexName($keys),
627
        ];
628
        if (isset($options['unique'])) {
629
            $doc['unique'] = $options['unique'];
630
        }
631
        if (isset($options['sparse'])) {
632
            $doc['sparse'] = $options['sparse'];
633
        }
634
        if (isset($options['version'])) {
635
            $doc['v'] = $options['version'];
636
        }
637
        if (isset($options['background'])) {
638
            $doc['background'] = $options['background'];
639
        }
640
        if (isset($options['ttl'])) {
641
            $doc['expireAfterSeconds'] = $options['ttl'];
642
        }
643
        $this->getCollection($e[0] . '.system.indexes')->insert($doc, $cb);
644
    }
645
646
    /**
647
     * Gets last error
648
     * @param  string $db Dbname
649
     * @param  callable $cb Callback called when response received
650
     * @param  array $params Parameters.
651
     * @param  Connection $conn Connection. Optional
0 ignored issues
show
Documentation introduced by
Should the type for parameter $conn not be Connection|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
652
     * @callback $cb ( )
653
     * @return void
654
     */
655
    public function lastError($db, $cb, $params = [], $conn = null)
656
    {
657
        $e = explode('.', $db, 2);
658
        $params['getlasterror'] = 1;
659
        $cb = CallbackWrapper::wrap($cb);
660
        try {
661
            $this->request(
662
                self::OP_QUERY,
663
                pack('V', 0) . $e[0] . '.$cmd' . "\x00"
664
                . pack('VV', 0, -1) . bson_encode($params),
665
                true,
666
                $conn,
667 View Code Duplication
                function ($conn, $reqId = null) use ($db, $cb) {
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...
668
                    if (!$conn) {
669
                        !$cb || $cb(['$err' => 'Connection error.']);
670
                        return;
671
                    }
672
                    $conn->requests[$reqId] = [$db, $cb, true];
673
                }
674
            );
675
        } catch (\MongoException $e) {
676
            Daemon::log('MongoClient exception: ' . $e->getMessage() . ': ' . $e->getTraceAsString());
677
            if ($cb !== null) {
678
                $cb(['$err' => $e->getMessage()]);
679
            }
680
        }
681
    }
682
683
    /**
684
     * Find objects in collection using min/max specifiers
685
     * @param  array $p Hash of properties (offset, limit, opts, where, col, min, max)
686
     * @param  callable $cb Callback called when response received
687
     * @callback $cb ( )
688
     * @return void
689
     */
690
    public function range($p, $cb)
691
    {
692
        if (!isset($p['offset'])) {
693
            $p['offset'] = 0;
694
        }
695
696
        if (!isset($p['limit'])) {
697
            $p['limit'] = -1;
698
        }
699
700
        if (!isset($p['opts'])) {
701
            $p['opts'] = 0;
702
        }
703
704
        if (!isset($p['where'])) {
705
            $p['where'] = [];
706
        }
707
708
        if (!isset($p['min'])) {
709
            $p['min'] = [];
710
        }
711
712
        if (!isset($p['max'])) {
713
            $p['max'] = [];
714
        }
715
716 View Code Duplication
        if (mb_orig_strpos($p['col'], '.') === false) {
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...
717
            $p['col'] = $this->dbname . '.' . $p['col'];
718
        }
719
720
        $e = explode('.', $p['col'], 2);
721
722
        $query = [
723
            '$query' => $p['where'],
724
        ];
725
726
        if (sizeof($p['min'])) {
727
            $query['$min'] = $p['min'];
728
        }
729
730
        if (sizeof($p['max'])) {
731
            $query['$max'] = $p['max'];
732
        }
733
734 View Code Duplication
        if (is_string($p['where'])) {
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...
735
            $query['where'] = new \MongoCode($p['where']);
736
        } elseif (is_object($p['where']) || sizeof($p['where'])) {
737
            $query['query'] = $p['where'];
738
        }
739
740
        $cb = CallbackWrapper::wrap($cb);
741
        if ($this->safeMode) {
742
            static::safeModeEnc($query);
743
        }
744
        try {
745
            $this->request(
746
                self::OP_QUERY,
747
                pack('V', $p['opts']) . $e[0] . '.$cmd' . "\x00"
748
                . pack('VV', $p['offset'], $p['limit']) . bson_encode($query)
749
                . (isset($p['fields']) ? bson_encode($p['fields']) : ''),
750
                true,
751
                null,
752 View Code Duplication
                function ($conn, $reqId = null) use ($p, $cb) {
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...
753
                    if (!$conn) {
754
                        !$cb || $cb(['$err' => 'Connection error.']);
755
                        return;
756
                    }
757
                    $conn->requests[$reqId] = [$p['col'], $cb, true];
758
                }
759
            );
760
        } catch (\MongoException $e) {
761
            Daemon::log('MongoClient exception: ' . $e->getMessage() . ': ' . $e->getTraceAsString());
762 View Code Duplication
            if ($cb !== null) {
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...
763
                $cb([
764
                    '$err' => $e->getMessage(),
765
                    '$query' => $query,
766
                    '$fields' => isset($p['fields']) ? $p['fields'] : null
767
                ]);
768
            }
769
        }
770
    }
771
772
    /**
773
     * Evaluates a code on the server side
774
     * @param  string $code Code
775
     * @param  callable $cb Callback called when response received
776
     * @callback $cb ( )
777
     * @return void
778
     */
779
    public function evaluate($code, $cb)
780
    {
781
        $p = [];
782
783
        if (!isset($p['offset'])) {
784
            $p['offset'] = 0;
785
        }
786
787
        if (!isset($p['limit'])) {
788
            $p['limit'] = -1;
789
        }
790
791
        if (!isset($p['opts'])) {
792
            $p['opts'] = 0;
793
        }
794
795
        if (!isset($p['db'])) {
796
            $p['db'] = $this->dbname;
797
        }
798
799
        $cb = CallbackWrapper::wrap($cb);
800
        try {
801
            $this->request(
802
                self::OP_QUERY,
803
                pack('V', $p['opts']) . $p['db'] . '.$cmd' . "\x00"
804
                . pack('VV', $p['offset'], $p['limit']) . bson_encode(['$eval' => new \MongoCode($code)])
805
                . (isset($p['fields']) ? bson_encode($p['fields']) : ''),
806
                true,
807
                null,
808 View Code Duplication
                function ($conn, $reqId = null) use ($p, $cb) {
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...
809
                    if (!$conn) {
810
                        !$cb || $cb(['$err' => 'Connection error.']);
811
                        return;
812
                    }
813
                    $conn->requests[$reqId] = [$p['db'], $cb, true];
814
                }
815
            );
816
        } catch (\MongoException $e) {
817
            Daemon::log('MongoClient exception: ' . $e->getMessage() . ': ' . $e->getTraceAsString());
818 View Code Duplication
            if ($cb !== null) {
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...
819
                $cb([
820
                    '$err' => $e->getMessage(),
821
                    '$query' => $query,
0 ignored issues
show
Bug introduced by
The variable $query does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
822
                    '$fields' => isset($p['fields']) ? $p['fields'] : null
823
                ]);
824
            }
825
        }
826
    }
827
828
    /**
829
     * Returns distinct values of the property
830
     * @param  array $p Hash of properties (offset, limit, opts, key, col, where)
831
     * @param  callable $cb Callback called when response received
832
     * @callback $cb ( )
833
     * @return void
834
     */
835
    public function distinct($p, $cb)
836
    {
837
        $this->_params($p);
838
        $e = explode('.', $p['col'], 2);
839
840
        $query = [
841
            'distinct' => $e[1],
842
            'key' => $p['key'],
843
        ];
844
845 View Code Duplication
        if (isset($p[$k = 'rp'])) {
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...
846
            $v = $p[$k];
847
            if (is_string($v)) {
848
                $v = ['mode' => $v];
849
            }
850
            $query['$readPreference'] = $v;
851
        }
852
853
        if (isset($p['where'])) {
854
            $query['query'] = $p['where'];
855
        }
856
        $cb = CallbackWrapper::wrap($cb);
857
        $this->request(
858
            self::OP_QUERY,
859
            pack('V', $p['opts']) . $e[0] . '.$cmd' . "\x00"
860
            . pack('VV', $p['offset'], $p['limit']) . bson_encode($query)
861
            . (isset($p['fields']) ? bson_encode($p['fields']) : ''),
862
            true,
863
            null,
864 View Code Duplication
            function ($conn, $reqId = null) use ($p, $cb) {
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...
865
                if (!$conn) {
866
                    !$cb || $cb(['$err' => 'Connection error.']);
867
                    return;
868
                }
869
                $conn->requests[$reqId] = [$p['col'], $cb, true];
870
            }
871
        );
872
    }
873
874
    /**
875
     * [_paramFields description]
876
     * @param  mixed $f
877
     * @return array
0 ignored issues
show
Documentation introduced by
Should the return type not be null|array?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
878
     */
879
    protected function _paramFields($f)
880
    {
881
        if (is_string($f)) {
882
            $f = array_map('trim', explode(',', $f));
883
        }
884
        if (!is_array($f) || sizeof($f) === 0) {
885
            return null;
886
        }
887
        if (!isset($f[0])) {
888
            return $f;
889
        }
890
        $p = [];
891
        foreach ($f as $k) {
892
            $p[$k] = 1;
893
        }
894
        return $p;
895
    }
896
897
    /**
898
     * [_params description]
899
     * @param  array &$p
900
     * @return void
901
     */
902
    protected function _params(&$p)
903
    {
904
        foreach ($p as $k => &$v) {
905
            if ($k === 'fields' || $k === 'sort') {
906
                $v = $this->_paramFields($v);
907
            } elseif ($k === 'where') {
908
                if (is_string($v)) {
909
                    $v = new \MongoCode($v);
910
                }
911
            } elseif ($k === 'reduce') {
912
                if (is_string($v)) {
913
                    $v = new \MongoCode($v);
914
                }
915
            } elseif ($k === 'rp') {
916
                if (is_string($v)) {
917
                    $v = ['mode' => $v];
918
                }
919
            }
920
        }
921
922
        if (!isset($p['offset'])) {
923
            $p['offset'] = 0;
924
        }
925
926
        if (!isset($p['limit'])) {
927
            $p['limit'] = -1;
928
        }
929
930
        if (!isset($p['opts'])) {
931
            $p['opts'] = 0;
932
        }
933
934
        if (!isset($p['key'])) {
935
            $p['key'] = '';
936
        }
937
938 View Code Duplication
        if (mb_orig_strpos($p['col'], '.') === false) {
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...
939
            $p['col'] = $this->dbname . '.' . $p['col'];
940
        }
941
    }
942
943
    /**
944
     * Find and modify
945
     * @param  array $p Hash of properties
946
     * @param  callable $cb Callback called when response received
947
     * @callback $cb ( )
948
     * @return void
949
     */
950
    public function findAndModify($p, $cb)
951
    {
952
        $this->_params($p);
953
        $e = explode('.', $p['col'], 2);
954
        $query = [
955
            'findAndModify' => $e[1],
956
        ];
957
958 View Code Duplication
        if (isset($p[$k = 'rp'])) {
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...
959
            $v = $p[$k];
960
            if (is_string($v)) {
961
                $v = ['mode' => $v];
962
            }
963
            $query['$readPreference'] = $v;
964
        }
965
966
        if (isset($p['sort'])) {
967
            $query['sort'] = $p['sort'];
968
        }
969
        if (isset($p['update'])) {
970
            $query['update'] = $p['update'];
971
        }
972
        if (isset($p['new'])) {
973
            $query['new'] = (boolean)$p['new'];
974
        }
975
        if (isset($p['remove'])) {
976
            $query['remove'] = (boolean)$p['remove'];
977
        }
978
        if (isset($p['upsert'])) {
979
            $query['upsert'] = (boolean)$p['upsert'];
980
        }
981
        if (isset($p['where'])) {
982
            $query['query'] = $p['where'];
983
        } elseif (isset($p['query'])) {
984
            $query['query'] = $p['query'];
985
        }
986
        if ($this->safeMode) {
987
            static::safeModeEnc($query);
988
        }
989
        $cb = CallbackWrapper::wrap($cb);
990
        try {
991
            $this->request(
992
                self::OP_QUERY,
993
                pack('V', $p['opts']) . $e[0] . '.$cmd' . "\x00"
994
                . pack('VV', $p['offset'], $p['limit']) . bson_encode($query)
995
                . (isset($p['fields']) ? bson_encode($p['fields']) : ''),
996
                true,
997
                null,
998 View Code Duplication
                function ($conn, $reqId = null) use ($p, $cb) {
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...
999
                    if (!$conn) {
1000
                        !$cb || $cb(['$err' => 'Connection error.']);
1001
                        return;
1002
                    }
1003
                    $conn->requests[$reqId] = [$p['col'], $cb, true];
1004
                }
1005
            );
1006
        } catch (\MongoException $e) {
1007
            Daemon::log('MongoClient exception: ' . $e->getMessage() . ': ' . $e->getTraceAsString());
1008 View Code Duplication
            if ($cb !== null) {
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...
1009
                $cb([
1010
                    '$err' => $e->getMessage(),
1011
                    '$query' => $query,
1012
                    '$fields' => isset($p['fields']) ? $p['fields'] : null
1013
                ]);
1014
            }
1015
        }
1016
    }
1017
1018
    /**
1019
     * Groupping function
1020
     * @param  array $p Hash of properties (offset, limit, opts, key, col, reduce, initial)
1021
     * @param  callable $cb Callback called when response received
1022
     * @callback $cb ( )
1023
     * @return void
1024
     */
1025
    public function group($p, $cb)
1026
    {
1027
        if (!isset($p['reduce'])) {
1028
            $p['reduce'] = '';
1029
        }
1030
        $this->_params($p);
1031
1032
        $e = explode('.', $p['col'], 2);
1033
1034
        $query = [
1035
            'group' => [
1036
                'ns' => $e[1],
1037
                'key' => $p['key'],
1038
                '$reduce' => $p['reduce'],
1039
                'initial' => $p['initial'],
1040
            ]
1041
        ];
1042
1043
        if (isset($p[$k = 'cond'])) {
1044
            $query['group'][$k] = $p[$k];
1045
        }
1046
1047
        if (isset($p['rp'])) {
1048
            $query['$readPreference'] = $p['rp'];
1049
        }
1050
1051
        if (isset($p[$k = 'finalize'])) {
1052
            if (is_string($p[$k])) {
1053
                $p[$k] = new \MongoCode($p[$k]);
1054
            }
1055
1056
            $query['group'][$k] = $p[$k];
1057
        }
1058
1059
        if (isset($p[$k = 'keyf'])) {
1060
            $query[$k] = $p[$k];
1061
        }
1062
        if ($this->safeMode) {
1063
            static::safeModeEnc($query);
1064
        }
1065
        $cb = CallbackWrapper::wrap($cb);
1066
        try {
1067
            $this->request(
1068
                self::OP_QUERY,
1069
                pack('V', $p['opts']) . $e[0] . '.$cmd' . "\x00"
1070
                . pack('VV', $p['offset'], $p['limit']) . bson_encode($query)
1071
                . (isset($p['fields']) ? bson_encode($p['fields']) : ''),
1072
                true,
1073
                null,
1074 View Code Duplication
                function ($conn, $reqId = null) use ($p, $cb) {
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...
1075
                    if (!$conn) {
1076
                        !$cb || $cb(['$err' => 'Connection error.']);
1077
                        return;
1078
                    }
1079
                    $conn->requests[$reqId] = [$p['col'], $cb, false];
1080
                }
1081
            );
1082
        } catch (\MongoException $e) {
1083
            Daemon::log('MongoClient exception: ' . $e->getMessage() . ': ' . $e->getTraceAsString());
1084 View Code Duplication
            if ($cb !== null) {
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...
1085
                $cb([
1086
                    '$err' => $e->getMessage(),
1087
                    '$query' => $query,
1088
                    '$fields' => isset($p['fields']) ? $p['fields'] : null
1089
                ]);
1090
            }
1091
        }
1092
    }
1093
1094
    /**
1095
     * Aggregate function
1096
     * @param  array $p Hash of properties (offset, limit, opts, key, col)
1097
     * @param  callable $cb Callback called when response received
1098
     * @callback $cb ( )
1099
     * @return void
1100
     */
1101
    public function aggregate($p, $cb)
1102
    {
1103
        $this->_params($p);
1104
1105
        $e = explode('.', $p['col'], 2);
1106
        $query = [
1107
            'aggregate' => $e[1]
1108
        ];
1109
1110
        if (isset($p['rp'])) {
1111
            $query['$readPreference'] = $p['rp'];
1112
            unset($p['rp']);
1113
        }
1114
        foreach ($p as $k => $v) {
1115
            if (substr($k, 0, 1) === '$' || $k === 'pipeline') {
1116
                $query[$k] = $v;
1117
            }
1118
        }
1119
        $cb = CallbackWrapper::wrap($cb);
1120
        try {
1121
            $this->request(
1122
                self::OP_QUERY,
1123
                pack('V', $p['opts']) . $e[0] . '.$cmd' . "\x00" . pack('VV', $p['offset'], $p['limit'])
1124
                . bson_encode($query) . (isset($p['fields']) ? bson_encode($p['fields']) : ''),
1125
                true,
1126
                null,
1127 View Code Duplication
                function ($conn, $reqId = null) use ($p, $cb) {
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...
1128
                    if (!$conn) {
1129
                        !$cb || $cb(['$err' => 'Connection error.']);
1130
                        return;
1131
                    }
1132
                    $conn->requests[$reqId] = [$p['col'], $cb, false];
1133
                }
1134
            );
1135
        } catch (\MongoException $e) {
1136
            Daemon::log('MongoClient exception: ' . $e->getMessage() . ': ' . $e->getTraceAsString());
1137 View Code Duplication
            if ($cb !== null) {
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...
1138
                $cb([
1139
                    '$err' => $e->getMessage(),
1140
                    '$query' => $query,
1141
                    '$fields' => isset($p['fields']) ? $p['fields'] : null
1142
                ]);
1143
            }
1144
        }
1145
    }
1146
1147
    /**
1148
     * Updates one object in collection
1149
     * @param  string $col Collection's name
1150
     * @param  array $cond Conditions
1151
     * @param  array $data Data
1152
     * @param  integer $flags Optional. Flags
1153
     * @param  callable $cb Optional. Callback
0 ignored issues
show
Documentation introduced by
Should the type for parameter $cb not be callable|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
1154
     * @param  array $params Optional. Parameters
1155
     * @callback $cb ( )
1156
     * @return void
1157
     */
1158
    public function update($col, $cond, $data, $flags = 0, $cb = null, $params = [])
1159
    {
1160 View Code Duplication
        if (mb_orig_strpos($col, '.') === false) {
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...
1161
            $col = $this->dbname . '.' . $col;
1162
        }
1163
1164
        if (is_string($cond)) {
1165
            $cond = new \MongoCode($cond);
1166
        }
1167
1168
        if ($flags) {
0 ignored issues
show
Unused Code introduced by
This if statement is empty and can be removed.

This check looks for the bodies of if statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

These if bodies can be removed. If you have an empty if but statements in the else branch, consider inverting the condition.

if (rand(1, 6) > 3) {
//print "Check failed";
} else {
    print "Check succeeded";
}

could be turned into

if (rand(1, 6) <= 3) {
    print "Check succeeded";
}

This is much more concise to read.

Loading history...
1169
            //if (!isset($data['_id'])) {$data['_id'] = new MongoId();}
0 ignored issues
show
Unused Code Comprehensibility introduced by
75% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
1170
        }
1171
        if ($this->safeMode) {
1172
            static::safeModeEnc($cond);
0 ignored issues
show
Bug introduced by
It seems like $cond defined by new \MongoCode($cond) on line 1165 can also be of type object<MongoCode>; however, PHPDaemon\Clients\Mongo\Pool::safeModeEnc() does only seem to accept array, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
1173
            static::safeModeEnc($data);
1174
        }
1175
1176
        $this->request(
1177
            self::OP_UPDATE,
1178
            "\x00\x00\x00\x00" . $col . "\x00" . pack('V', $flags) . bson_encode($cond) . bson_encode($data),
1179
            false,
1180
            null,
1181 View Code Duplication
            function ($conn, $reqId = null) use ($cb, $col, $params) {
0 ignored issues
show
Unused Code introduced by
The parameter $reqId 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...
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...
1182
                if (!$conn) {
1183
                    !$cb || $cb(['$err' => 'Connection error.']);
1184
                    return;
1185
                }
1186
                if ($cb !== null) {
1187
                    $this->lastError($col, $cb, $params, $conn);
1188
                }
1189
            }
1190
        );
1191
    }
1192
1193
    /**
1194
     * Updates one object in collection
1195
     * @param  string $col Collection's name
1196
     * @param  array $cond Conditions
1197
     * @param  array $data Data
1198
     * @param  callable $cb Optional. Callback
0 ignored issues
show
Documentation introduced by
Should the type for parameter $cb not be callable|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
1199
     * @param  array $params Optional. Parameters
1200
     * @callback $cb ( )
1201
     * @return void
1202
     */
1203
    public function updateOne($col, $cond, $data, $cb = null, $params = [])
1204
    {
1205
        $this->update($col, $cond, $data, 0, $cb, $params);
1206
    }
1207
1208
    /**
1209
     * Updates several objects in collection
1210
     * @param  string $col Collection's name
1211
     * @param  array $cond Conditions
1212
     * @param  array $data Data
1213
     * @param  callable $cb Optional. Callback
0 ignored issues
show
Documentation introduced by
Should the type for parameter $cb not be callable|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
1214
     * @param  array $params Optional. Parameters
1215
     * @callback $cb ( )
1216
     * @return void
1217
     */
1218
    public function updateMulti($col, $cond, $data, $cb = null, $params = [])
1219
    {
1220
        $this->update($col, $cond, $data, 2, $cb, $params);
1221
    }
1222
1223
    /**
1224
     * Upserts an object (updates if exists, insert if not exists)
1225
     * @param  string $col Collection's name
1226
     * @param  array $cond Conditions
1227
     * @param  array $data Data
1228
     * @param  boolean $multi Optional. Multi
1229
     * @param  callable $cb Optional. Callback
0 ignored issues
show
Documentation introduced by
Should the type for parameter $cb not be callable|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
1230
     * @param  array $params Optional. Parameters
1231
     * @callback $cb ( )
1232
     * @return void
1233
     */
1234
    public function upsert($col, $cond, $data, $multi = false, $cb = null, $params = [])
1235
    {
1236
        $this->update($col, $cond, $data, $multi ? 3 : 1, $cb, $params);
1237
    }
1238
1239
    /**
1240
     * Upserts an object (updates if exists, insert if not exists)
1241
     * @param  string $col Collection's name
1242
     * @param  array $cond Conditions
1243
     * @param  array $data Data
1244
     * @param  callable $cb Optional. Callback
0 ignored issues
show
Documentation introduced by
Should the type for parameter $cb not be callable|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
1245
     * @param  array $params Optional. Parameters
1246
     * @callback $cb ( )
1247
     * @return void
1248
     */
1249
    public function upsertOne($col, $cond, $data, $cb = null, $params = [])
1250
    {
1251
        $this->update($col, $cond, $data, 1, $cb, $params);
1252
    }
1253
1254
    /**
1255
     * Upserts an object (updates if exists, insert if not exists)
1256
     * @param  string $col Collection's name
1257
     * @param  array $cond Conditions
1258
     * @param  array $data Data
1259
     * @param  callable $cb Optional. Callback
0 ignored issues
show
Documentation introduced by
Should the type for parameter $cb not be callable|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
1260
     * @param  array $params Optional. Parameters
1261
     * @callback $cb ( )
1262
     * @return void
1263
     */
1264
    public function upsertMulti($col, $cond, $data, $cb = null, $params = [])
1265
    {
1266
        $this->update($col, $cond, $data, 3, $cb, $params);
1267
    }
1268
1269
    /**
1270
     * Inserts an object
1271
     * @param  string $col Collection's name
1272
     * @param  array $doc Document
1273
     * @param  callable $cb Optional. Callback
0 ignored issues
show
Documentation introduced by
Should the type for parameter $cb not be callable|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
1274
     * @param  array $params Optional. Parameters
1275
     * @callback $cb ( )
1276
     * @return MongoId
1277
     */
1278
    public function insert($col, $doc = [], $cb = null, $params = [])
1279
    {
1280 View Code Duplication
        if (mb_orig_strpos($col, '.') === false) {
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...
1281
            $col = $this->dbname . '.' . $col;
1282
        }
1283
1284
        if (!isset($doc['_id'])) {
1285
            $doc['_id'] = new \MongoId;
1286
        }
1287
        if ($this->safeMode) {
1288
            static::safeModeEnc($doc);
1289
        }
1290
        try {
1291
            $this->request(
1292
                self::OP_INSERT,
1293
                "\x00\x00\x00\x00" . $col . "\x00" . bson_encode($doc),
1294
                false,
1295
                null,
1296 View Code Duplication
                function ($conn, $reqId = null) use ($cb, $col, $params) {
0 ignored issues
show
Unused Code introduced by
The parameter $reqId 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...
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...
1297
                    if ($cb !== null) {
1298
                        $this->lastError($col, $cb, $params, $conn);
1299
                    }
1300
                }
1301
            );
1302
1303
            return $doc['_id'];
1304
        } catch (\MongoException $e) {
1305
            Daemon::log('MongoClient exception: ' . $e->getMessage() . ': ' . $e->getTraceAsString());
1306
            if ($cb !== null) {
1307
                if ($cb !== null) {
1308
                    $cb(['$err' => $e->getMessage(), '$doc' => $doc]);
1309
                }
1310
            }
1311
        }
1312
    }
1313
1314
    /**
1315
     * Sends a request to kill certain cursors on the server side
1316
     * @param  array $cursors Array of cursors
1317
     * @param  Connection $conn Connection
1318
     * @return void
1319
     */
1320
    public function killCursors($cursors, $conn)
1321
    {
1322
        if (!$cursors) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $cursors of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
1323
            $cursors = [];
1324
        }
1325
1326
        $this->request(
1327
            self::OP_KILL_CURSORS,
1328
            "\x00\x00\x00\x00" . pack('V', sizeof($cursors)) . implode('', $cursors),
1329
            false,
1330
            $conn
1331
        );
1332
    }
1333
1334
    /**
1335
     * Inserts several documents
1336
     * @param  string $col Collection's name
1337
     * @param  array $docs Array of docs
1338
     * @param  callable $cb Optional. Callback
0 ignored issues
show
Documentation introduced by
Should the type for parameter $cb not be callable|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
1339
     * @param  array $params Optional. Parameters
1340
     * @callback $cb ( )
1341
     * @return array IDs
1342
     */
1343
    public function insertMulti($col, $docs = [], $cb = null, $params = [])
1344
    {
1345 View Code Duplication
        if (mb_orig_strpos($col, '.') === false) {
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...
1346
            $col = $this->dbname . '.' . $col;
1347
        }
1348
1349
        $ids = [];
1350
        $bson = '';
1351
1352
        foreach ($docs as &$doc) {
1353
            if (!isset($doc['_id'])) {
1354
                $doc['_id'] = new MongoId();
1355
            }
1356
            try {
1357
                $bson .= bson_encode($doc);
1358
            } catch (\MongoException $e) {
1359
                Daemon::log('MongoClient exception: ' . $e->getMessage() . ': ' . $e->getTraceAsString());
1360
                if ($cb !== null) {
1361
                    $cb(['$err' => $e->getMessage(), '$doc' => $doc]);
1362
                }
1363
            }
1364
1365
            $ids[] = $doc['_id'];
1366
        }
1367
1368
        $this->request(
1369
            self::OP_INSERT,
1370
            "\x00\x00\x00\x00" . $col . "\x00" . $bson,
1371
            false,
1372
            null,
1373 View Code Duplication
            function ($conn, $reqId = null) use ($cb, $col, $params) {
0 ignored issues
show
Unused Code introduced by
The parameter $reqId 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...
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...
1374
                if ($cb !== null) {
1375
                    $this->lastError($col, $cb, $params, $conn);
1376
                }
1377
            }
1378
        );
1379
1380
        return $ids;
1381
    }
1382
1383
    /**
1384
     * Remove objects from collection
1385
     * @param  string $col Collection's name
1386
     * @param  array $cond Conditions
1387
     * @param  callable $cb Optional. Callback called when response received
0 ignored issues
show
Documentation introduced by
Should the type for parameter $cb not be callable|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
1388
     * @param  array $params Optional. Parameters
1389
     * @callback $cb ( )
1390
     * @return void
1391
     */
1392
    public function remove($col, $cond = [], $cb = null, $params = [])
1393
    {
1394 View Code Duplication
        if (mb_orig_strpos($col, '.') === false) {
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...
1395
            $col = $this->dbname . '.' . $col;
1396
        }
1397
1398
        if (is_string($cond)) {
1399
            $cond = new \MongoCode($cond);
1400
        }
1401
1402
        if ($this->safeMode && is_array($cond)) {
1403
            static::safeModeEnc($cond);
1404
        }
1405
        try {
1406
            $this->request(
1407
                self::OP_DELETE,
1408
                "\x00\x00\x00\x00" . $col . "\x00" . "\x00\x00\x00\x00" . bson_encode($cond),
1409
                false,
1410
                null,
1411 View Code Duplication
                function ($conn, $reqId = null) use ($col, $cb, $params) {
0 ignored issues
show
Unused Code introduced by
The parameter $reqId 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...
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...
1412
                    if (!$conn) {
1413
                        !$cb || $cb(['$err' => 'Connection error.']);
1414
                        return;
1415
                    }
1416
                    if ($cb !== null) {
1417
                        $this->lastError($col, $cb, $params, $conn);
1418
                    }
1419
                }
1420
            );
1421
        } catch (\MongoException $e) {
1422
            Daemon::log('MongoClient exception: ' . $e->getMessage() . ': ' . $e->getTraceAsString());
1423
            if ($cb !== null) {
1424
                $cb(['$err' => $e->getMessage(), '$query' => $cond]);
1425
            }
1426
        }
1427
    }
1428
1429
    /**
1430
     * Asks for more objects
1431
     * @param  string $col Collection's name
1432
     * @param  string $id Cursor's ID
1433
     * @param  integer $number Number of objects
1434
     * @param  Connection $conn Connection
1435
     * @return void
1436
     */
1437
    public function getMore($col, $id, $number, $conn)
1438
    {
1439 View Code Duplication
        if (mb_orig_strpos($col, '.') === false) {
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...
1440
            $col = $this->dbname . '.' . $col;
1441
        }
1442
1443
        $this->request(
1444
            self::OP_GETMORE,
1445
            "\x00\x00\x00\x00" . $col . "\x00" . pack('V', $number) . $id,
1446
            false,
1447
            $conn,
1448
            function ($conn, $reqId = null) use ($id) {
1449
                if (!$conn) {
1450
                    !$cb || $cb(['$err' => 'Connection error.']);
0 ignored issues
show
Bug introduced by
The variable $cb does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
1451
                    return;
1452
                }
1453
                $conn->requests[$reqId] = [$id];
1454
            }
1455
        );
1456
    }
1457
1458
    /**
1459
     * Returns an object of collection
1460
     * @param  string $col Collection's name
1461
     * @return Collection
1462
     */
1463
    public function getCollection($col)
1464
    {
1465
        if (mb_orig_strpos($col, '.') === false) {
1466
            $col = $this->dbname . '.' . $col;
1467
        } else {
1468
            $collName = explode('.', $col, 2);
0 ignored issues
show
Unused Code introduced by
$collName 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...
1469
        }
1470
1471
        if (isset($this->collections[$col])) {
1472
            return $this->collections[$col];
1473
        }
1474
1475
        return $this->collections[$col] = new Collection($col, $this);
1476
    }
1477
1478
    /**
1479
     * Magic getter-method. Proxy for getCollection
1480
     * @param  string $name Collection's name
1481
     * @return Collection
1482
     */
1483
    public function __get($name)
1484
    {
1485
        return $this->getCollection($name);
1486
    }
1487
1488
    public function saslScrumSHA1Auth($p, $cb)
1489
    {
1490
        $session = [
1491
            'cb' => $cb,
1492
            'step' => 0,
1493
            'dbname' => $p['dbname'],
1494
            'user' => $p['user'],
1495
            'password' => $p['password'],
1496
            'auth_message' => '',
1497
            'conn' => array_key_exists('conn', $p) ? $p['conn'] : null,
1498
        ];
1499
        $this->saslScrumSHA1Step($session);
1500
    }
1501
1502
    public function saslScrumSHA1Step($session, $input = null)
1503
    {
1504
        $session['step']++;
1505
        $query = [];
1506
1507
        if (!is_null($input) && (!empty($input['$err']) || !empty($input['errmsg']))) {
1508
            $session['cb']($input);
1509
            return;
1510
        }
1511
1512
        if ($session['step'] == 1) {
1513
            $session['nonce'] = base64_encode(openssl_random_pseudo_bytes(24));
1514
            $payload = 'n,,n=' . $session['user'] . ',r=' . $session['nonce'];
1515
            $query = ['saslStart' => 1, 'mechanism' => 'SCRAM-SHA-1', 'payload' => base64_encode($payload)];
1516
            $session['auth_message'] .= 'n=' . $session['user'] . ',r=' . $session['nonce'] . ',';
1517
        } elseif ($session['step'] == 2) {
1518
            $in_payload = $this->saslScrumSHA1ExtractPayload($input['payload']);
1519
1520
            $error = null;
1521
            if (count($in_payload) != 3) {
1522
                $error = 'Incorrect number of arguments for first SCRAM-SHA-1 server message, got ' . count($in_payload) . 'expected 3';
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 136 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
1523
            } elseif (mb_orig_strlen($in_payload['r']) < 2) {
1524
                $error = 'Incorrect SCRAM-SHA-1 client|server nonce: ' . $in_payload['r'];
1525
            } elseif (mb_orig_strlen($in_payload['s']) < 6) {
1526
                $error = 'Incorrect SCRAM-SHA-1 salt: ' . $in_payload['s'];
1527
            } elseif (mb_orig_strlen($in_payload['i']) < 3) {
1528
                $error = 'Incorrect SCRAM-SHA-1 iteration count: ' . $in_payload['i'];
1529
            } elseif (mb_orig_strpos($in_payload['r'], $session['nonce']) !== 0) {
1530
                $error = 'Server SCRAM-SHA-1 nonce does not match client nonce';
1531
            }
1532
            if (!empty($error)) {
1533
                $session['cb'](['ok' => 0, 'errmsg' => $error]);
1534
                return;
1535
            } else {
1536
                $session['conversation_id'] = $input['conversationId'];
1537
                $session['nonce'] = $in_payload['r'];
1538
            }
1539
1540
            $payload = 'c=biws,r=' . $session['nonce'];
1541
            $session['auth_message'] .= base64_decode($input['payload']) . ',' . $payload;
1542
1543
            $decoded_salt = base64_decode($in_payload['s']);
1544
            $password = md5($session['user'] . ':mongo:' . $session['password']);
1545
            $salted_password = hash_pbkdf2('sha1', $password, $decoded_salt, (int)$in_payload['i'], 0, true);
1546
1547
            $client_key = hash_hmac('sha1', 'Client Key', $salted_password, true);
1548
            $stored_key = sha1($client_key, true);
1549
            $client_sign = hash_hmac('sha1', $session['auth_message'], $stored_key, true);
1550
            $client_proof = $client_key ^ $client_sign;
1551
1552
            $payload .= ',p=' . base64_encode($client_proof);
1553
1554
            $query = [
1555
                'saslContinue' => 1,
1556
                'conversationId' => $session['conversation_id'],
1557
                'payload' => base64_encode($payload)
1558
            ];
1559
        } elseif ($session['step'] == 3) {
1560
            $in_payload = $this->saslScrumSHA1ExtractPayload($input['payload']);
1561
            if (!empty($in_payload['v'])) {
1562
                $session['server_signature'] = $in_payload['v'];
1563
                $query = [
1564
                    'saslContinue' => 1,
1565
                    'conversationId' => $session['conversation_id'],
1566
                    'payload' => base64_encode('')
1567
                ];
1568
            }
1569
        } elseif ($session['step'] == 4) {
1570
            $in_payload = $this->saslScrumSHA1ExtractPayload($input['payload']);
0 ignored issues
show
Unused Code introduced by
$in_payload 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...
1571
            $res = $input['done'] ? [
1572
                'ok' => 1,
1573
                'server_signature' => $session['server_signature'],
1574
            ] : [
1575
                'ok' => 0,
1576
                'errmsg' => 'Authentication failed.',
1577
            ];
1578
            $session['cb']($res);
1579
            return;
1580
        }
1581
1582
        $this->saslScrumSHA1Conversation($session['dbname'], $query, function ($res) use ($session) {
1583
            $this->saslScrumSHA1Step($session, $res);
1584
        }, $session['conn']);
1585
    }
1586
1587
    public function saslScrumSHA1Conversation($dbname, $query, $cb, $conn = null)
1588
    {
1589
        if ($this->safeMode) {
1590
            static::safeModeEnc($query);
1591
        }
1592
1593
        try {
1594
            $this->request(
1595
                self::OP_QUERY,
1596
                pack('V', 0) . $dbname . '.$cmd' . "\x00" . pack('VV', 0, -1) . bson_encode($query),
1597
                true,
1598
                $conn,
1599 View Code Duplication
                function ($conn, $reqId = null) use ($dbname, $cb) {
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...
1600
                    if (!$conn) {
1601
                        !$cb || $cb(['$err' => 'Connection error.']);
1602
                        return;
1603
                    }
1604
                    $conn->requests[$reqId] = [$dbname, $cb, true];
1605
                }
1606
            );
1607
        } catch (\MongoException $e) {
1608
            Daemon::log('MongoClient exception: ' . $e->getMessage() . ': ' . $e->getTraceAsString());
1609 View Code Duplication
            if ($cb !== null) {
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...
1610
                $cb([
1611
                    '$err' => $e->getMessage(),
1612
                    '$query' => $query,
1613
                    '$fields' => isset($p['fields']) ? $p['fields'] : null
0 ignored issues
show
Bug introduced by
The variable $p seems to never exist, and therefore isset should always return false. Did you maybe rename this variable?

This check looks for calls to isset(...) or empty() on variables that are yet undefined. These calls will always produce the same result and can be removed.

This is most likely caused by the renaming of a variable or the removal of a function/method parameter.

Loading history...
1614
                ]);
1615
            }
1616
        }
1617
    }
1618
1619
    public function saslScrumSHA1ExtractPayload($payload)
1620
    {
1621
        $result = [];
1622
        $payload = base64_decode($payload);
1623
        foreach (explode(',', $payload) as $line) {
1624
            if (preg_match('/^([a-z]+)=(.*)/', $line, $ms)) {
1625
                $result[$ms[1]] = $ms[2];
1626
            }
1627
        }
1628
        return $result;
1629
    }
1630
}
1631