Connection::onReady()   A
last analyzed

Complexity

Conditions 5
Paths 3

Size

Total Lines 23

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
dl 0
loc 23
rs 9.2408
c 0
b 0
f 0
nc 3
nop 0
1
<?php
2
namespace PHPDaemon\Clients\Redis;
3
4
use PHPDaemon\Core\CallbackWrapper;
5
use PHPDaemon\Core\Daemon;
6
use PHPDaemon\Core\Debug;
7
use PHPDaemon\Network\ClientConnection;
8
9
/**
10
 * @package    NetworkClients
11
 * @subpackage RedisClient
12
 * @author     Vasily Zorin <[email protected]>
13
 */
14
class Connection extends ClientConnection implements \Iterator
15
{
16
    /**
17
     * Default flag
18
     */
19
    const RESULT_TYPE_DEFAULT = 0;
20
21
    /**
22
     * Flag - parse message response
23
     */
24
    const RESULT_TYPE_MESSAGE = 1;
25
26
    /**
27
     * Flag - parse response with arguments
28
     */
29
    const RESULT_TYPE_ARGSVALS = 2;
30
31
    /**
32
     * Flag - parse response to associative array
33
     */
34
    const RESULT_TYPE_ASSOC = 3;
35
36
    /**
37
     * @var array|null Current result
38
     */
39
    public $result = null;
40
41
    /**
42
     * @var string Channel name
43
     */
44
    public $channel = null;
45
46
    /**
47
     * @var string Message
48
     */
49
    public $msg = null;
50
51
    /**
52
     * @var string Current error message
53
     */
54
    public $error;
55
56
    /**
57
     * @var array Subcriptions
58
     */
59
    public $subscribeCb = [];
60
61
    public $psubscribeCb = [];
62
63
    /**
64
     * @var string Current incoming key
65
     */
66
    protected $key;
67
68
    protected $stack = [];
69
70
    protected $ptr;
71
72
    /**
73
     * @var integer Current value length
74
     */
75
    protected $valueLength = 0;
76
77
    /**
78
     * @var integer Current level length
79
     */
80
    protected $levelLength = null;
81
82
    /**
83
     * @var string
84
     */
85
    protected $EOL = "\r\n";
86
87
    /**
88
     * @var boolean Is it a subscription connection?
89
     */
90
    protected $subscribed = false;
91
92
    /**
93
     * @var float Timeout
94
     */
95
    protected $timeoutRead = 120;
96
97
    /**
98
     * @var integer Iterator position
99
     */
100
    protected $pos = 0;
101
102
    /**
103
     * @var \SplStack Stack of results types
104
     */
105
    protected $resultTypeStack;
106
107
    /**
108
     * @var null|array Current result type
109
     */
110
    protected $resultType = 0;
111
112
    /**
113
     * @var \SplStack Stack of commands arguments
114
     */
115
    protected $argsStack;
116
117
    /**
118
     * @var null|array Current arguments
119
     */
120
    protected $args = null;
121
122
    /**
123
     * @var null|array Associative result storage
124
     */
125
    protected $assocData = null;
126
127
    /**
128
     * In the middle of binary response part
129
     */
130
    const STATE_BINARY = 1;
131
132
    public function __construct($fd, $pool = null)
133
    {
134
        parent::__construct($fd, $pool);
135
        $this->resultTypeStack = new \SplStack;
136
        $this->argsStack = new \SplStack;
137
    }
138
139
    public function rewind()
140
    {
141
        $this->pos = 0;
142
    }
143
144 View Code Duplication
    public function current()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
145
    {
146
        if (!is_array($this->result)) {
147
            return $this->pos === 0 ? $this->result : null;
148
        } elseif ($this->resultType === static::RESULT_TYPE_DEFAULT || $this->resultType === static::RESULT_TYPE_ARGSVALS) {
149
            return $this->result[$this->pos];
150
        } elseif ($this->resultType === static::RESULT_TYPE_MESSAGE) {
151
            // message
152
            return $this->result[2];
153
        } elseif ($this->resultType === static::RESULT_TYPE_ASSOC) {
154
            return $this->result[$this->pos * 2 + 1];
155
        }
156
    }
157
158 View Code Duplication
    public function key()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
159
    {
160
        if (!is_array($this->result)) {
161
            return $this->pos === 0 ? 0 : null;
162
        } elseif ($this->resultType === static::RESULT_TYPE_DEFAULT) {
163
            return $this->pos;
164
        } elseif ($this->resultType === static::RESULT_TYPE_MESSAGE) {
165
            // channel
166
            return $this->result[1];
167
        } elseif ($this->resultType === static::RESULT_TYPE_ARGSVALS) {
168
            return $this->args[$this->pos];
169
        } elseif ($this->resultType === static::RESULT_TYPE_ASSOC) {
170
            return $this->result[$this->pos * 2];
171
        }
172
    }
173
174
    public function next()
175
    {
176
        ++$this->pos;
177
    }
178
179
    public function valid()
180
    {
181
        if (!is_array($this->result)) {
182
            return false;
183
        } elseif ($this->resultType === static::RESULT_TYPE_DEFAULT) {
184
            return isset($this->result[$this->pos]);
185
        } elseif ($this->resultType === static::RESULT_TYPE_ARGSVALS) {
186
            return isset($this->args[$this->pos]) && isset($this->result[$this->pos]);
187
        } elseif ($this->resultType === static::RESULT_TYPE_MESSAGE || $this->resultType === static::RESULT_TYPE_ASSOC) {
188
            return isset($this->result[$this->pos * 2 + 1]);
189
        }
190
    }
191
192
    /**
193
     * Get associative result
194
     * @param  string $name
195
     * @return array
0 ignored issues
show
Documentation introduced by
Should the return type not be array|null?

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...
196
     */
197
    public function __get($name)
198
    {
199
        if ($name === 'assoc') {
200
            if ($this->assocData === null) {
201
                if (!is_array($this->result) || empty($this->result)) {
202
                    $this->assocData = [];
203
                } elseif ($this->resultType === static::RESULT_TYPE_MESSAGE) {
204
                    $this->assocData = [$this->result[1] => $this->result[2]];
205
                } elseif ($this->resultType === static::RESULT_TYPE_ARGSVALS) {
206
                    $hash = [];
207 View Code Duplication
                    for ($i = 0, $s = sizeof($this->result); $i < $s; ++$i) {
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...
208
                        $hash[$this->args[$i]] = $this->result[$i];
209
                    }
210
                    $this->assocData = $hash;
211
                } elseif ($this->resultType === static::RESULT_TYPE_ASSOC) {
212
                    $hash = [];
213 View Code Duplication
                    for ($i = 0, $s = sizeof($this->result) - 1; $i < $s; ++$i) {
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...
214
                        $hash[$this->result[$i]] = $this->result[++$i];
215
                    }
216
                    $this->assocData = $hash;
217
                } else {
218
                    $this->assocData = $this->result;
219
                }
220
            }
221
            return $this->assocData;
222
        }
223
    }
224
225
    /**
226
     * Converts array into hash
227
     * @param array $array
228
     * @return array $hash
229
     */
230
    public function arrayToHash($array)
231
    {
232
        $hash = [];
233 View Code Duplication
        for ($i = 0, $s = sizeof($array) - 1; $i < $s; ++$i) {
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...
234
            $hash[$array[$i]] = $array[++$i];
235
        }
236
        return $hash;
237
    }
238
239
    /**
240
     * @TODO
241
     * @param  string $key
242
     * @param  integer $timeout
243
     * @return Lock
244
     */
245
    public function lock($key, $timeout)
246
    {
247
        return new Lock($key, $timeout, $this);
0 ignored issues
show
Documentation introduced by
$this is of type this<PHPDaemon\Clients\Redis\Connection>, but the function expects a object<PHPDaemon\Clients\Redis\Pool>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
248
    }
249
250
    /**
251
     * Easy wrapper for queue of eval's
252
     * @param  callable $cb
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...
253
     * @return MultiEval
254
     */
255
    public function meval($cb = null)
256
    {
257
        return new MultiEval($cb, $this);
0 ignored issues
show
Bug introduced by
It seems like $cb defined by parameter $cb on line 255 can also be of type null; however, PHPDaemon\Clients\Redis\MultiEval::__construct() does only seem to accept callable, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
Documentation introduced by
$this is of type this<PHPDaemon\Clients\Redis\Connection>, but the function expects a object<PHPDaemon\Clients\Redis\Pool>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
258
    }
259
260
    /**
261
     * Wrapper for scans commands
262
     * @param  string $cmd Command
263
     * @param  array $args Arguments
264
     * @param  cllable $cbEnd Callback
0 ignored issues
show
Documentation introduced by
Should the type for parameter $cbEnd not be cllable|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...
265
     * @param  integer $limit Limit
0 ignored issues
show
Documentation introduced by
Should the type for parameter $limit 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...
266
     * @return AutoScan
267
     */
268
    public function autoscan($cmd, $args = [], $cbEnd = null, $limit = null)
269
    {
270
        return new AutoScan($this, $cmd, $args, $cbEnd, $limit);
0 ignored issues
show
Documentation introduced by
$this is of type this<PHPDaemon\Clients\Redis\Connection>, but the function expects a object<PHPDaemon\Clients\Redis\Pool>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
271
    }
272
273
    /**
274
     * @TODO
275
     * @param  string $chan
276
     * @return integer
277
     */
278
    public function getLocalSubscribersCount($chan)
279
    {
280
        if (!isset($this->subscribeCb[$chan])) {
281
            return 0;
282
        }
283
        return sizeof($this->subscribeCb[$chan]);
284
    }
285
286
    /**
287
     * Called when the connection is handshaked (at low-level), and peer is ready to recv. data
288
     * @return void
289
     */
290
    public function onReady()
291
    {
292
        $this->ptr =& $this->result;
293
        if (!isset($this->password)) {
294
            if (isset($this->pool->config->select->value)) {
295
                $this->select($this->pool->config->select->value);
0 ignored issues
show
Documentation Bug introduced by
The method select does not exist on object<PHPDaemon\Clients\Redis\Connection>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
296
            }
297
            parent::onReady();
298
            $this->setWatermark(null, $this->pool->maxAllowedPacket + 2);
299
            return;
300
        }
301
        $this->sendCommand('AUTH', [$this->password], function () {
302
            if ($this->result !== 'OK') {
303
                $this->log('Auth. error: ' . json_encode($this->result));
304
                $this->finish();
305
            }
306
            if (isset($this->pool->config->select->value)) {
307
                $this->select($this->pool->config->select->value);
0 ignored issues
show
Documentation Bug introduced by
The method select does not exist on object<PHPDaemon\Clients\Redis\Connection>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
308
            }
309
            parent::onReady();
310
            $this->setWatermark(null, $this->pool->maxAllowedPacket + 2);
311
        });
312
    }
313
314
    /**
315
     * Magic __call
316
     * Example:
317
     * $redis->lpush('mylist', microtime(true));
318
     * @param  sting $cmd
319
     * @param  array $args
320
     * @return void
321
     */
322
    public function __call($cmd, $args)
323
    {
324
        $cb = null;
325 View Code Duplication
        for ($i = sizeof($args) - 1; $i >= 0; --$i) {
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...
326
            $a = $args[$i];
327
            if ((is_array($a) || is_object($a)) && is_callable($a)) {
328
                $cb = CallbackWrapper::wrap($a);
329
                $args = array_slice($args, 0, $i);
330
                break;
331
            } elseif ($a !== null) {
332
                break;
333
            }
334
        }
335
        $cmd = strtoupper($cmd);
336
        $this->command($cmd, $args, $cb);
337
    }
338
339
    /**
340
     * @TODO
341
     * @param  string $name
342
     * @param  array $args
343
     * @param  callable $cb
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...
344
     * @callback $cb ( )
345
     * @return void
346
     */
347
    public function command($name, $args, $cb = null)
348
    {
349
        if ($name === 'MULTI' || $name === 'WATCH') {
350
            $this->acquire();
351
        } // PUB/SUB handling
352
        elseif (mb_orig_substr($name, -9) === 'SUBSCRIBE') {
353
            if (!$this->subscribed) {
354
                $this->subscribed = true;
355
                $this->pool->servConnSub[$this->url] = $this;
356
                $this->acquire();
357
                $this->setTimeouts(86400, 86400); // @TODO: remove timeout
358
            }
359
360
            $opcb = null;
361 View Code Duplication
            for ($i = sizeof($args) - 1; $i >= 0; --$i) {
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...
362
                $a = $args[$i];
363
                if ((is_array($a) || is_object($a)) && is_callable($a)) {
364
                    $opcb = $cb;
365
                    $cb = CallbackWrapper::wrap($a);
366
                    $args = array_slice($args, 0, $i);
367
                    break;
368
                } elseif ($a !== null) {
369
                    break;
370
                }
371
            }
372
        }
373
374
        if ($name === 'SUBSCRIBE') {
375
            $this->subscribed();
0 ignored issues
show
Documentation Bug introduced by
The method subscribed does not exist on object<PHPDaemon\Clients\Redis\Connection>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
376
            $channels = [];
377 View Code Duplication
            foreach ($args as $arg) {
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...
378
                if (!is_array($arg)) {
379
                    $arg = [$arg];
380
                }
381
                foreach ($arg as $chan) {
382
                    $b = !isset($this->subscribeCb[$chan]);
383
                    CallbackWrapper::addToArray($this->subscribeCb[$chan], $cb);
384
                    if ($b) {
385
                        $channels[] = $chan;
386
                    } else {
387
                        if ($opcb !== null) {
388
                            $opcb($this);
0 ignored issues
show
Bug introduced by
The variable $opcb does not seem to be defined for all execution paths leading up to this point.

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

Let’s take a look at an example:

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

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

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

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

Available Fixes

  1. Check for existence of the variable explicitly:

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

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

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
389
                        }
390
                    }
391
                }
392
            }
393
            if (sizeof($channels)) {
394
                $this->sendCommand($name, $channels, $opcb);
395
            }
396
        } elseif ($name === 'PSUBSCRIBE') {
397
            $this->subscribed();
0 ignored issues
show
Documentation Bug introduced by
The method subscribed does not exist on object<PHPDaemon\Clients\Redis\Connection>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
398
            $channels = [];
399 View Code Duplication
            foreach ($args as $arg) {
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...
400
                if (!is_array($arg)) {
401
                    $arg = [$arg];
402
                }
403
                foreach ($arg as $chan) {
404
                    $b = !isset($this->psubscribeCb[$chan]);
405
                    CallbackWrapper::addToArray($this->psubscribeCb[$chan], $cb);
406
                    if ($b) {
407
                        $channels[] = $chan;
408
                    } else {
409
                        if ($opcb !== null) {
410
                            $opcb($this);
411
                        }
412
                    }
413
                }
414
            }
415
            if (sizeof($channels)) {
416
                $this->sendCommand($name, $channels, $opcb);
417
            }
418
        } elseif ($name === 'UNSUBSCRIBE') {
419
            $channels = [];
420
            foreach ($args as $arg) {
421
                if (!is_array($arg)) {
422
                    $arg = [$arg];
423
                }
424
                foreach ($arg as $chan) {
425
                    if (!isset($this->subscribeCb[$chan])) {
426
                        if ($opcb !== null) {
427
                            $opcb($this);
428
                        }
429
                        return;
430
                    }
431
                    CallbackWrapper::removeFromArray($this->subscribeCb[$chan], $cb);
432 View Code Duplication
                    if (sizeof($this->subscribeCb[$chan]) === 0) {
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...
433
                        $channels[] = $chan;
434
                        unset($this->subscribeCb[$chan]);
435
                    } else {
436
                        if ($opcb !== null) {
437
                            $opcb($this);
438
                        }
439
                    }
440
                }
441
            }
442
            if (sizeof($channels)) {
443
                $this->sendCommand($name, $channels, $opcb);
444
            }
445 View Code Duplication
        } elseif ($name === 'UNSUBSCRIBEREAL') {
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
            /* Race-condition-free UNSUBSCRIBE */
447
            $old = $this->subscribeCb;
448
            $this->sendCommand('UNSUBSCRIBE', $args, function ($redis) use ($cb, $args, $old) {
449
                if (!$redis) {
450
                    $cb($redis);
451
                    return;
452
                }
453
                foreach ($args as $arg) {
454
                    if (!isset($this->subscribeCb[$arg])) {
455
                        continue;
456
                    }
457
                    foreach ($old[$arg] as $oldcb) {
458
                        CallbackWrapper::removeFromArray($this->subscribeCb[$arg], $oldcb);
459
                    }
460
                    if (!sizeof($this->subscribeCb[$arg])) {
461
                        unset($this->subscribeCb[$arg]);
462
                    }
463
                }
464
                if ($cb !== null) {
465
                    $cb($this);
466
                }
467
            });
468
        } elseif ($name === 'PUNSUBSCRIBE') {
469
            $channels = [];
470
            foreach ($args as $arg) {
471
                if (!is_array($arg)) {
472
                    $arg = [$arg];
473
                }
474
                foreach ($arg as $chan) {
475
                    CallbackWrapper::removeFromArray($this->psubscribeCb[$chan], $cb);
476 View Code Duplication
                    if (sizeof($this->psubscribeCb[$chan]) === 0) {
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...
477
                        $channels[] = $chan;
478
                        unset($this->psubscribeCb[$chan]);
479
                    } else {
480
                        if ($opcb !== null) {
481
                            $opcb($this);
482
                        }
483
                    }
484
                }
485
            }
486
            if (sizeof($channels)) {
487
                $this->sendCommand($name, $channels, $opcb);
488
            }
489 View Code Duplication
        } elseif ($name === 'PUNSUBSCRIBEREAL') {
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...
490
            /* Race-condition-free PUNSUBSCRIBE */
491
            $old = $this->psubscribeCb;
492
            $this->sendCommand('PUNSUBSCRIBE', $args, function ($redis) use ($cb, $args, $old) {
493
                if (!$redis) {
494
                    $cb($redis);
495
                    return;
496
                }
497
                foreach ($args as $arg) {
498
                    if (!isset($this->psubscribeCb[$arg])) {
499
                        continue;
500
                    }
501
                    foreach ($old[$arg] as $oldcb) {
502
                        CallbackWrapper::removeFromArray($this->psubscribeCb[$arg], $oldcb);
503
                    }
504
                    if (!sizeof($this->psubscribeCb[$arg])) {
505
                        unset($this->psubscribeCb[$arg]);
506
                    }
507
                }
508
                if ($cb !== null) {
509
                    $cb($this);
510
                }
511
            });
512
        } else {
513
            if ($name === 'MGET') {
514
                $this->resultTypeStack->push(static::RESULT_TYPE_ARGSVALS);
515
                $this->argsStack->push($args);
516
            } elseif ($name === 'HMGET') {
517
                $this->resultTypeStack->push(static::RESULT_TYPE_ARGSVALS);
518
                $a = $args;
519
                array_shift($a);
520
                $this->argsStack->push($a);
521
            } elseif ($name === 'HMSET') {
522
                if (sizeof($args) === 2) {
523
                    if (is_array($args[1])) {
524
                        $newArgs = [$args[0]];
525
                        foreach ($args[1] as $key => $value) {
526
                            $newArgs[] = $key;
527
                            $newArgs[] = $value;
528
                        }
529
                        $args = $newArgs;
530
                    }
531
                }
532
            } elseif ($name === 'HGETALL') {
533
                $this->resultTypeStack->push(static::RESULT_TYPE_ASSOC);
534
            } elseif (($name === 'ZRANGE' || $name === 'ZRANGEBYSCORE' || $name === 'ZREVRANGE' || $name === 'ZREVRANGEBYSCORE')
535
                && preg_grep('/WITHSCORES/i', $args)
536
            ) {
537
                $this->resultTypeStack->push(static::RESULT_TYPE_ASSOC);
538
            } else {
539
                $this->resultTypeStack->push(static::RESULT_TYPE_DEFAULT);
540
            }
541
542
            $this->sendCommand($name, $args, $cb);
543
544
            if ($name === 'EXEC' || $name === 'DISCARD') {
545
                $this->release();
546
            }
547
        }
548
    }
549
550
    /**
551
     * @TODO
552
     * @param  string $name
553
     * @param  array $args
554
     * @param  callable $cb
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...
555
     * @callback $cb ( )
556
     * @return void
557
     */
558
    public function sendCommand($name, $args, $cb = null)
559
    {
560
        if ($name === 'MULTI') {
561
            $this->onResponse(null);
0 ignored issues
show
Documentation introduced by
null is of type null, but the function expects a callable.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
562
        } else {
563
            $this->onResponse($cb);
0 ignored issues
show
Bug introduced by
It seems like $cb defined by parameter $cb on line 558 can also be of type null; however, PHPDaemon\Network\ClientConnection::onResponse() does only seem to accept callable, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
564
        }
565
        if (!is_array($args)) {
566
            $args = [$args];
567
        }
568
        array_unshift($args, $name);
569
        $this->writeln('*' . sizeof($args));
570
        foreach ($args as $arg) {
571
            $this->writeln('$' . mb_orig_strlen($arg) . $this->EOL . $arg);
572
        }
573
        if ($name === 'MULTI') {
574
            if ($cb !== null) {
575
                $cb($this);
576
            }
577
        }
578
    }
579
580
    /**
581
     * Called when connection finishes
582
     * @return void
583
     */
584
    public function onFinish()
585
    {
586
        parent::onFinish();
587
        if ($this->subscribed) {
588
            unset($this->pool->servConnSub[$this->url]);
589
        }
590
        /* we should reassign subscriptions */
591
        foreach ($this->subscribeCb as $sub => $cbs) {
592
            foreach ($cbs as $cb) {
593
                $this->pool->subscribe($sub, $cb);
594
            }
595
        }
596
        foreach ($this->psubscribeCb as $sub => $cbs) {
597
            foreach ($cbs as $cb) {
598
                $this->pool->psubscribe($sub, $cb);
599
            }
600
        }
601
    }
602
603
    protected function onPacket()
604
    {
605
        $this->result = $this->ptr;
606
        if (!$this->subscribed) {
607
            $this->resultType = !$this->resultTypeStack->isEmpty() ? $this->resultTypeStack->shift() : static::RESULT_TYPE_DEFAULT;
608 View Code Duplication
            if ($this->resultType === static::RESULT_TYPE_ARGSVALS) {
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...
609
                $this->args = !$this->argsStack->isEmpty() ? $this->argsStack->shift() : [];
610
            }
611
            $this->onResponse->executeOne($this);
612
            goto clean;
613
        } elseif ($this->result[0] === 'message') {
614
            $t = &$this->subscribeCb;
615
        } elseif ($this->result[0] === 'pmessage') {
616
            $t = &$this->psubscribeCb;
617
        } else {
618
            $this->resultType = !$this->resultTypeStack->isEmpty() ? $this->resultTypeStack->shift() : static::RESULT_TYPE_DEFAULT;
619 View Code Duplication
            if ($this->resultType === static::RESULT_TYPE_ARGSVALS) {
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...
620
                $this->args = !$this->argsStack->isEmpty() ? $this->argsStack->shift() : [];
621
            }
622
            $this->onResponse->executeOne($this);
623
            goto clean;
624
        }
625
        if (isset($t[$this->result[1]])) {
626
            $this->resultType = static::RESULT_TYPE_MESSAGE;
0 ignored issues
show
Documentation Bug introduced by
It seems like static::RESULT_TYPE_MESSAGE of type integer is incompatible with the declared type null|array of property $resultType.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
627
            $this->channel = $this->result[1];
628
            $this->msg = $this->result[2];
629
            foreach ($t[$this->result[1]] as $cb) {
630
                if (is_callable($cb)) {
631
                    $cb($this);
632
                }
633
            }
634
        } elseif ($this->pool->config->logpubsubracecondition->value) {
635
            Daemon::log('[Redis client]' . ': PUB/SUB race condition at channel ' . Debug::json($this->result[1]));
636
        }
637
        clean:
638
        $this->args = null;
639
        $this->result = null;
640
        $this->channel = null;
641
        $this->msg = null;
642
        $this->error = false;
0 ignored issues
show
Documentation Bug introduced by
The property $error was declared of type string, but false is of type false. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
643
        $this->pos = 0;
644
        $this->resultType = static::RESULT_TYPE_DEFAULT;
0 ignored issues
show
Documentation Bug introduced by
It seems like static::RESULT_TYPE_DEFAULT of type integer is incompatible with the declared type null|array of property $resultType.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
645
        $this->assocData = null;
646
        if (!isset($t)) {
647
            $this->checkFree();
648
        }
649
    }
650
651
    /**
652
     * @TODO
653
     * @param  mixed $val
654
     * @return void
655
     */
656
    public function pushValue($val)
657
    {
658
        if (is_array($this->ptr)) {
659
            $this->ptr[] = $val;
660
        } else {
661
            $this->ptr = $val;
662
        }
663
        start:
664
        $size = is_array($this->ptr) ? sizeof($this->ptr) : 1;
665
        if ($size < $this->levelLength) {
666
            return;
667
        }
668
        array_pop($this->stack);
669
        if (!sizeof($this->stack)) {
670
            $this->levelLength = null;
671
            $this->onPacket();
672
            $this->ptr =& $dummy;
0 ignored issues
show
Bug introduced by
The variable $dummy 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...
673
            $this->ptr = null;
674
            return;
675
        }
676
677
        $this->ptr =& $dummy;
678
679
        list($this->ptr, $this->levelLength) = end($this->stack);
680
681
        goto start;
682
    }
683
684
    /**
685
     * Called when new data received
686
     * @return void
687
     */
688
    protected function onRead()
689
    {
690
        start:
691
        if ($this->state === static::STATE_STANDBY) { // outside of packet
692
            while (($l = $this->readline()) !== null) {
693
                if ($l === '') {
694
                    continue;
695
                }
696
                $char = $l[0];
697
                $val = mb_orig_substr($l, 1);
698
                if ($char === ':') { // inline integer
699
                    $this->pushValue((int)$val);
700
                    goto start;
701
                } elseif (($char === '+') || ($char === '-')) { // inline string
702
                    $this->error = $char === '-';
0 ignored issues
show
Documentation Bug introduced by
The property $error was declared of type string, but $char === '-' is of type boolean. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
703
                    $this->pushValue($val);
704
                    goto start;
705
                } elseif ($char === '*') { // defines number of elements of incoming array
706
                    $length = (int)$val;
707
                    if ($length <= 0) {
708
                        $this->pushValue([]);
709
                        goto start;
710
                    }
711
712
                    $ptr = [];
713
714
                    if (is_array($this->ptr)) {
715
                        $this->ptr[] =& $ptr;
716
                    }
717
718
                    $this->ptr =& $ptr;
719
                    $this->stack[] = [&$ptr, $length];
720
                    $this->levelLength = $length;
721
                    unset($ptr);
722
723
                    goto start;
724
                } elseif ($char === '$') { // defines size of the data block
725
                    if ($l[1]=== '-') {
726
                        $this->pushValue(null);
727
                        goto start;
728
                    }
729
                    $this->valueLength = (int)$val;
730
                    if ($this->valueLength + 2 > $this->pool->maxAllowedPacket) {
731
                        $this->log('max-allowed-packet (' . $this->pool->config->maxallowedpacket->getHumanValue() . ') exceed, aborting connection');
732
                        $this->finish();
733
                        return;
734
                    }
735
                    $this->setWatermark($this->valueLength + 2);
736
                    $this->state = static::STATE_BINARY; // binary data block
737
                    break; // stop reading line-by-line
738
                }
739
            }
740
        }
741
742
        if ($this->state === static::STATE_BINARY) { // inside of binary data block
743
            if ($this->getInputLength() < $this->valueLength + 2) {
744
                return; //we do not have a whole packet
745
            }
746
            $value = $this->read($this->valueLength);
747
            if ($this->read(2) !== $this->EOL) {
748
                $this->finish();
749
                return;
750
            }
751
            $this->state = static::STATE_STANDBY;
752
            $this->setWatermark(3);
753
            $this->pushValue($value);
754
            goto start;
755
        }
756
    }
757
}
758