Completed
Pull Request — master (#6)
by Michal
02:10
created

RedisProxy::incrbyfloat()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 5
ccs 3
cts 3
cp 1
rs 9.4285
cc 1
eloc 3
nc 1
nop 2
crap 1
1
<?php
2
3
namespace RedisProxy;
4
5
use Exception;
6
use Predis\Client;
7
use Predis\Response\Status;
8
use Redis;
9
10
/**
11
 * @method mixed config(string $command, $argument = null)
12
 * @method int dbsize() Return the number of keys in the selected database
13
 * @method boolean restore(string $key, int $ttl, string $serializedValue) Create a key using the provided serialized value, previously obtained using DUMP. If ttl is 0 the key is created without any expire, otherwise the specified expire time (in milliseconds) is set
14
 * @method boolean set(string $key, string $value) Set the string value of a key
15
 * @method boolean setex(string $key, int $seconds, string $value) Set the value and expiration of a key
16
 * @method int ttl(string $key) Get the time to live for a key, returns TTL in seconds, -2 if the key does not exist, -1 if the key exists but has no associated expire
17
 * @method int pttl(string $key) Get the time to live for a key in milliseconds, returns TTL in miliseconds, -2 if the key does not exist, -1 if the key exists but has no associated expire
18
 * @method array keys(string $pattern) Find all keys matching the given pattern
19
 * @method int hset(string $key, string $field, string $value) Set the string value of a hash field
20
 * @method array hkeys(string $key) Get all fields in a hash (without values)
21
 * @method array hgetall(string $key) Get all fields and values in a hash
22
 * @method int hlen(string $key) Get the number of fields in a hash
23
 * @method array smembers(string $key) Get all the members in a set
24
 * @method int scard(string $key) Get the number of members in a set
25
 * @method boolean flushall() Remove all keys from all databases
26
 * @method boolean flushdb() Remove all keys from the current database
27
 */
28
class RedisProxy
29
{
30
    use SortedSetBehavior;
31
32
    use ListBehavior;
33
34
    const DRIVER_REDIS = 'redis';
35
36
    const DRIVER_PREDIS = 'predis';
37
38
    const TYPE_STRING = 'string';
39
40
    const TYPE_SET = 'set';
41
42
    const TYPE_HASH = 'hash';
43
44
    const TYPE_LIST = 'list';
45
46
    const TYPE_SORTED_SET = 'sorted_set';
47
48
    private $driver;
49
50
    private $host;
51
52
    private $port;
53
54
    private $database = 0;
55
56
    private $selectedDatabase = 0;
57
58
    private $timeout;
59
60
    private $supportedDrivers = [
61
        self::DRIVER_REDIS,
62
        self::DRIVER_PREDIS,
63
    ];
64
65
    private $driversOrder = [];
66
67
    private $redisTypeMap = [
68
        self::DRIVER_REDIS => [
69
            Redis::REDIS_STRING => self::TYPE_STRING,
70
            Redis::REDIS_SET => self::TYPE_SET,
71
            Redis::REDIS_HASH => self::TYPE_HASH,
72
            Redis::REDIS_LIST => self::TYPE_LIST,
73
            Redis::REDIS_ZSET => self::TYPE_SORTED_SET,
74
        ],
75
        self::DRIVER_PREDIS => [
76
            'string' => self::TYPE_STRING,
77
            'set' => self::TYPE_SET,
78
            'hash' => self::TYPE_HASH,
79
            'list' => self::TYPE_LIST,
80
            'zset' => self::TYPE_SORTED_SET,
81
        ],
82
    ];
83
84
    public function __construct($host, $port, $database = 0, $timeout = null)
85 260
    {
86
        $this->host = $host;
87 260
        $this->port = $port;
88 260
        $this->database = $database;
89 260
        $this->timeout = $timeout;
90 260
        $this->driversOrder = $this->supportedDrivers;
91 260
    }
92 260
93
    /**
94
     * Set driver priorities - default is 1. redis, 2. predis
95
     * @param array $driversOrder
96
     * @return RedisProxy
97
     * @throws RedisProxyException if some driver is not supported
98
     */
99
    public function setDriversOrder(array $driversOrder)
100 260
    {
101
        foreach ($driversOrder as $driver) {
102 260
            if (!in_array($driver, $this->supportedDrivers)) {
103 258
                throw new RedisProxyException('Driver "' . $driver . '" is not supported');
104 258
            }
105
        }
106
        $this->driversOrder = $driversOrder;
107 258
        return $this;
108 258
    }
109
110
    protected function init()
111 258
    {
112
        $this->prepareDriver();
113 258
        $this->select($this->database);
114 256
    }
115 256
116
    private function prepareDriver()
117 258
    {
118
        if ($this->driver !== null) {
119 258
            return;
120 256
        }
121
122
        foreach ($this->driversOrder as $preferredDriver) {
123 258
            if ($preferredDriver === self::DRIVER_REDIS && extension_loaded('redis')) {
124 256
                $this->driver = new Redis();
125 128
                return;
126 128
            }
127
            if ($preferredDriver === self::DRIVER_PREDIS && class_exists('Predis\Client')) {
128 128
                $this->driver = new Client();
129 128
                return;
130 128
            }
131
        }
132
        throw new RedisProxyException('No driver available');
133 2
    }
134
135
    /**
136
     * @return string|null
137
     */
138
    public function actualDriver()
139 258
    {
140
        if ($this->driver instanceof Redis) {
141 258
            return self::DRIVER_REDIS;
142 128
        }
143
        if ($this->driver instanceof Client) {
144 134
            return self::DRIVER_PREDIS;
145 128
        }
146
        return null;
147 10
    }
148
149
    private function connect($host, $port, $timeout = null)
150 256
    {
151
        return $this->driver->connect($host, $port, $timeout);
152 256
    }
153
154
    private function isConnected()
155 256
    {
156
        return $this->driver->isConnected();
157 256
    }
158
159
    public function __call($name, $arguments)
160 258
    {
161
        $this->init();
162 258
        $name = strtolower($name);
163 256
        try {
164
            $result = call_user_func_array([$this->driver, $name], $arguments);
165 256
        } catch (Exception $e) {
166 4
            throw new RedisProxyException("Error for command '$name', use getPrevious() for more info", 1484162284, $e);
167 4
        }
168
        return $this->transformResult($result);
169 256
    }
170
171
    /**
172
     * @param int $database
173
     * @return boolean true on success
174
     * @throws RedisProxyException on failure
175
     */
176
    public function select($database)
177 256
    {
178
        $this->prepareDriver();
179 256
        if (!$this->isConnected()) {
180 256
            $this->connect($this->host, $this->port, $this->timeout);
181 256
        }
182
        if ($database == $this->selectedDatabase) {
183 256
            return true;
184 256
        }
185
        try {
186
            $result = $this->driver->select($database);
187 12
        } catch (Exception $e) {
188 2
            throw new RedisProxyException('Invalid DB index');
189 2
        }
190
        $result = $this->transformResult($result);
191 10
        if ($result === false) {
192 10
            throw new RedisProxyException('Invalid DB index');
193 2
        }
194
        $this->database = $database;
195 8
        $this->selectedDatabase = $database;
196 8
        return $result;
197 8
    }
198
199
    /**
200
     * @param string|null $section
201
     * @return array
202
     */
203
    public function info($section = null)
204 8
    {
205
        $this->init();
206 8
        $section = $section ? strtolower($section) : $section;
207 8
        $result = $section === null ? $this->driver->info() : $this->driver->info($section);
208 8
209
        $databases = $section === null || $section === 'keyspace' ? $this->config('get', 'databases')['databases'] : null;
210 8
        $groupedResult = InfoHelper::createInfoArray($this, $result, $databases);
211 8
        if ($section === null) {
212 8
            return $groupedResult;
213 4
        }
214
        if (isset($groupedResult[$section])) {
215 8
            return $groupedResult[$section];
216 4
        }
217
        throw new RedisProxyException('Info section "' . $section . '" doesn\'t exist');
218 4
    }
219
220
    /**
221
     * Determine if a key exists
222
     * @param string $key
223
     * @return boolean
224
     */
225
    public function exists($key)
226 4
    {
227
        $this->init();
228 4
        $result = $this->driver->exists($key);
229 4
        return (bool)$result;
230 4
    }
231
232
    /**
233
     * @param string $key
234
     * @return string|null
235
     */
236
    public function type($key)
237 4
    {
238
        $this->init();
239 4
        $result = $this->driver->type($key);
240 4
        $result = $this->actualDriver() === self::DRIVER_PREDIS && $result instanceof Status ? $result->getPayload() : $result;
241 4
        return isset($this->redisTypeMap[$this->actualDriver()][$result]) ? $this->redisTypeMap[$this->actualDriver()][$result] : null;
242 4
    }
243
244
    /**
245
     * Get the value of a key
246
     * @param string $key
247
     * @return string|null null if key not set
248
     */
249
    public function get($key)
250 88
    {
251
        $this->init();
252 88
        $result = $this->driver->get($key);
253 88
        return $this->convertFalseToNull($result);
254 88
    }
255
256
    /**
257
     * Set the string value of a key and return its old value
258
     * @param string $key
259
     * @param string $value
260
     * @return string|null null if key was not set before
261
     */
262
    public function getset($key, $value)
263 4
    {
264
        $this->init();
265 4
        $result = $this->driver->getset($key, $value);
266 4
        return $this->convertFalseToNull($result);
267 4
    }
268
269
    /**
270
     * Set a key's time to live in seconds
271
     * @param string $key
272
     * @param int $seconds
273
     * @return boolean true if the timeout was set, false if key does not exist or the timeout could not be set
274
     */
275
    public function expire($key, $seconds)
276 12
    {
277
        $this->init();
278 12
        $result = $this->driver->expire($key, $seconds);
279 12
        return (bool)$result;
280 12
    }
281
282
    /**
283
     * Set a key's time to live in milliseconds
284
     * @param string $key
285
     * @param int $miliseconds
286
     * @return boolean true if the timeout was set, false if key does not exist or the timeout could not be set
287
     */
288
    public function pexpire($key, $miliseconds)
289 8
    {
290
        $this->init();
291 8
        $result = $this->driver->pexpire($key, $miliseconds);
292 8
        return (bool)$result;
293 8
    }
294
295
    /**
296
     * Set the expiration for a key as a UNIX timestamp
297
     * @param string $key
298
     * @param int $timestamp
299
     * @return boolean true if the timeout was set, false if key does not exist or the timeout could not be set
300
     */
301
    public function expireat($key, $timestamp)
302 4
    {
303
        $this->init();
304 4
        $result = $this->driver->expireat($key, $timestamp);
305 4
        return (bool)$result;
306 4
    }
307
308
    /**
309
     * Set the expiration for a key as a UNIX timestamp specified in milliseconds
310
     * @param string $key
311
     * @param int $milisecondsTimestamp
312
     * @return boolean true if the timeout was set, false if key does not exist or the timeout could not be set
313
     */
314
    public function pexpireat($key, $milisecondsTimestamp)
315 4
    {
316
        $this->init();
317 4
        $result = $this->driver->pexpireat($key, $milisecondsTimestamp);
318 4
        return (bool)$result;
319 4
    }
320
321
    /**
322
     * Set the value and expiration in milliseconds of a key
323
     * @param string $key
324
     * @param int $miliseconds
325
     * @param string $value
326
     * @return boolean
327
     */
328
    public function psetex($key, $miliseconds, $value)
329 4
    {
330
        $this->init();
331 4
        $result = $this->driver->psetex($key, $miliseconds, $value);
332 4
        if ($result == '+OK') {
333 4
            return true;
334 2
        }
335
        return $this->transformResult($result);
336 2
    }
337
338
    /**
339
     * Remove the expiration from a key
340
     * @param string $key
341
     * @return boolean
342
     */
343
    public function persist($key)
344 4
    {
345
        $this->init();
346 4
        $result = $this->driver->persist($key);
347 4
        return (bool)$result;
348 4
    }
349
350
    /**
351
     * Set the value of a key, only if the key does not exist
352
     * @param string $key
353
     * @param string $value
354
     * @return boolean true if the key was set, false if the key was not set
355
     */
356
    public function setnx($key, $value)
357 4
    {
358
        $this->init();
359 4
        $result = $this->driver->setnx($key, $value);
360 4
        return (bool)$result;
361 4
    }
362
363
    /**
364
     * Delete a key(s)
365
     * @param array $keys
366
     * @return int number of deleted keys
367
     */
368
    public function del(...$keys)
369 32
    {
370
        $this->prepareArguments('del', ...$keys);
371 32
        $this->init();
372 24
        return $this->driver->del(...$keys);
373 24
    }
374
375
    /**
376
     * Delete a key(s)
377
     * @param array $keys
378
     * @return int number of deleted keys
379
     */
380
    public function delete(...$keys)
381 12
    {
382
        return $this->del(...$keys);
383 12
    }
384
385
    /**
386
     * Increment the integer value of a key by one
387
     * @param string $key
388
     * @return integer
389
     */
390
    public function incr($key)
391 4
    {
392
        $this->init();
393 4
        return $this->driver->incr($key);
394 4
    }
395
396
    /**
397
     * Increment the integer value of a key by the given amount
398
     * @param string $key
399
     * @param integer $increment
400
     * @return integer
401
     */
402
    public function incrby($key, $increment = 1)
403 4
    {
404
        $this->init();
405 4
        return $this->driver->incrby($key, (int)$increment);
406 4
    }
407
408
    /**
409
     * Increment the float value of a key by the given amount
410
     * @param string $key
411
     * @param float $increment
412
     * @return float
413
     */
414
    public function incrbyfloat($key, $increment = 1)
415 8
    {
416
        $this->init();
417 8
        return $this->driver->incrbyfloat($key, $increment);
418 8
    }
419
420
    /**
421
     * Decrement the integer value of a key by one
422
     * @param string $key
423
     * @return integer
424
     */
425
    public function decr($key)
426 4
    {
427
        $this->init();
428 4
        return $this->driver->decr($key);
429 4
    }
430
431
    /**
432
     * Decrement the integer value of a key by the given number
433
     * @param string $key
434
     * @param integer $decrement
435
     * @return integer
436
     */
437
    public function decrby($key, $decrement = 1)
438 4
    {
439
        $this->init();
440 4
        return $this->driver->decrby($key, (int)$decrement);
441 4
    }
442
443
    /**
444
     * Decrement the float value of a key by the given amount
445
     * @param string $key
446
     * @param float $decrement
447
     * @return float
448
     */
449
    public function decrbyfloat($key, $decrement = 1)
450 4
    {
451
        return $this->incrbyfloat($key, (-1) * $decrement);
452 4
    }
453
454
    /**
455
     * Return a serialized version of the value stored at the specified key
456
     * @param string $key
457
     * @return string|null serialized value, null if key doesn't exist
458
     */
459
    public function dump($key)
460 4
    {
461
        $this->init();
462 4
        $result = $this->driver->dump($key);
463 4
        return $this->convertFalseToNull($result);
464 4
    }
465
466
    /**
467
     * Set multiple values to multiple keys
468
     * @param array $dictionary
469
     * @return boolean true on success
470
     * @throws RedisProxyException if number of arguments is wrong
471
     */
472 View Code Duplication
    public function mset(...$dictionary)
473 12
    {
474
        $this->init();
475 12
        if (is_array($dictionary[0])) {
476 12
            $result = $this->driver->mset(...$dictionary);
477 8
            return $this->transformResult($result);
478 8
        }
479
        $dictionary = $this->prepareKeyValue($dictionary, 'mset');
480 8
        $result = $this->driver->mset($dictionary);
481 4
        return $this->transformResult($result);
482 4
    }
483
484
    /**
485
     * Multi get
486
     * @param array $keys
487
     * @return array Returns the values for all specified keys. For every key that does not hold a string value or does not exist, null is returned
488
     */
489
    public function mget(...$keys)
490 4
    {
491
        $keys = array_unique($this->prepareArguments('mget', ...$keys));
492 4
        $this->init();
493 4
        $values = [];
494 4
        foreach ($this->driver->mget($keys) as $value) {
495 4
            $values[] = $this->convertFalseToNull($value);
496 4
        }
497
        return array_combine($keys, $values);
498 4
    }
499
500
    /**
501
     * Incrementally iterate the keys space
502
     * @param mixed $iterator iterator / cursor, use $iterator = null for start scanning, when $iterator is changed to 0 or '0', scanning is finished
503
     * @param string $pattern pattern for keys, use * as wild card
504
     * @param int $count
505
     * @return array|boolean|null list of found keys, returns null if $iterator is 0 or '0'
506
     */
507 View Code Duplication
    public function scan(&$iterator, $pattern = null, $count = null)
508 4
    {
509
        if ((string)$iterator === '0') {
510 4
            return null;
511 4
        }
512
        $this->init();
513 4
        if ($this->actualDriver() === self::DRIVER_PREDIS) {
514 4
            $returned = $this->driver->scan($iterator, ['match' => $pattern, 'count' => $count]);
515 2
            $iterator = $returned[0];
516 2
            return $returned[1];
517 2
        }
518
        return $this->driver->scan($iterator, $pattern, $count);
519 2
    }
520
521
    /**
522
     * Get the value of a hash field
523
     * @param string $key
524
     * @param string $field
525
     * @return string|null null if hash field is not set
526
     */
527
    public function hget($key, $field)
528 16
    {
529
        $this->init();
530 16
        $result = $this->driver->hget($key, $field);
531 16
        return $this->convertFalseToNull($result);
532 16
    }
533
534
    /**
535
     * Delete one or more hash fields, returns number of deleted fields
536
     * @param array $key
537
     * @param array $fields
538
     * @return int
539
     */
540
    public function hdel($key, ...$fields)
541 8
    {
542
        $fields = $this->prepareArguments('hdel', ...$fields);
543 8
        $this->init();
544 8
        return $this->driver->hdel($key, ...$fields);
545 8
    }
546
547
    /**
548
     * Increment the integer value of hash field by given number
549
     * @param string $key
550
     * @param string $field
551
     * @param int $increment
552
     * @return int
553
     */
554
    public function hincrby($key, $field, $increment = 1)
555 4
    {
556
        $this->init();
557 4
        return $this->driver->hincrby($key, $field, (int)$increment);
558 4
    }
559
560
    /**
561
     * Increment the float value of hash field by given amount
562
     * @param string $key
563
     * @param string $field
564
     * @param float $increment
565
     * @return float
566
     */
567
    public function hincrbyfloat($key, $field, $increment = 1)
568 4
    {
569
        $this->init();
570 4
        return $this->driver->hincrbyfloat($key, $field, $increment);
571 4
    }
572
573
    /**
574
     * Set multiple values to multiple hash fields
575
     * @param string $key
576
     * @param array $dictionary
577
     * @return boolean true on success
578
     * @throws RedisProxyException if number of arguments is wrong
579
     */
580 View Code Duplication
    public function hmset($key, ...$dictionary)
581 12
    {
582
        $this->init();
583 12
        if (is_array($dictionary[0])) {
584 12
            $result = $this->driver->hmset($key, ...$dictionary);
585 8
            return $this->transformResult($result);
586 8
        }
587
        $dictionary = $this->prepareKeyValue($dictionary, 'hmset');
588 8
        $result = $this->driver->hmset($key, $dictionary);
589 4
        return $this->transformResult($result);
590 4
    }
591
592
    /**
593
     * Multi hash get
594
     * @param string $key
595
     * @param array $fields
596
     * @return array Returns the values for all specified fields. For every field that does not hold a string value or does not exist, null is returned
597
     */
598
    public function hmget($key, ...$fields)
599 4
    {
600
        $fields = array_unique($this->prepareArguments('hmget', ...$fields));
601 4
        $this->init();
602 4
        $values = [];
603 4
        foreach ($this->driver->hmget($key, $fields) as $value) {
604 4
            $values[] = $this->convertFalseToNull($value);
605 4
        }
606
        return array_combine($fields, $values);
607 4
    }
608
609
    /**
610
     * Incrementally iterate hash fields and associated values
611
     * @param string $key
612
     * @param mixed $iterator iterator / cursor, use $iterator = null for start scanning, when $iterator is changed to 0 or '0', scanning is finished
613
     * @param string $pattern pattern for fields, use * as wild card
614
     * @param int $count
615
     * @return array|boolean|null list of found fields with associated values, returns null if $iterator is 0 or '0'
616
     */
617 View Code Duplication
    public function hscan($key, &$iterator, $pattern = null, $count = null)
618 4
    {
619
        if ((string)$iterator === '0') {
620 4
            return null;
621 4
        }
622
        $this->init();
623 4
        if ($this->actualDriver() === self::DRIVER_PREDIS) {
624 4
            $returned = $this->driver->hscan($key, $iterator, ['match' => $pattern, 'count' => $count]);
625 2
            $iterator = $returned[0];
626 2
            return $returned[1];
627 2
        }
628
        return $this->driver->hscan($key, $iterator, $pattern, $count);
629 2
    }
630
631
    /**
632
     * Add one or more members to a set
633
     * @param string $key
634
     * @param array $members
635
     * @return int number of new members added to set
636
     */
637
    public function sadd($key, ...$members)
638 16
    {
639
        $members = $this->prepareArguments('sadd', ...$members);
640 16
        $this->init();
641 16
        return $this->driver->sadd($key, ...$members);
642 16
    }
643
644
    /**
645
     * Remove and return one or multiple random members from a set
646
     * @param string $key
647
     * @param int $count number of members
648
     * @return mixed string if $count is null or 1 and $key exists, array if $count > 1 and $key exists, null if $key doesn't exist
649
     */
650
    public function spop($key, $count = 1)
651 4
    {
652
        $this->init();
653 4
        if ($count == 1 || $count === null) {
654 4
            $result = $this->driver->spop($key);
655 4
            return $this->convertFalseToNull($result);
656 4
        }
657
658
        $members = [];
659 4
        for ($i = 0; $i < $count; ++$i) {
660 4
            $member = $this->driver->spop($key);
661 4
            if (!$member) {
662 4
                break;
663 4
            }
664
            $members[] = $member;
665 4
        }
666
        return empty($members) ? null : $members;
667 4
    }
668
669
    /**
670
     * Incrementally iterate Set elements
671
     * @param string $key
672
     * @param mixed $iterator iterator / cursor, use $iterator = null for start scanning, when $iterator is changed to 0 or '0', scanning is finished
673
     * @param string $pattern pattern for member's values, use * as wild card
674
     * @param int $count
675
     * @return array|boolean|null list of found members, returns null if $iterator is 0 or '0'
676
     */
677 View Code Duplication
    public function sscan($key, &$iterator, $pattern = null, $count = null)
678 4
    {
679
        if ((string)$iterator === '0') {
680 4
            return null;
681 4
        }
682
        $this->init();
683 4
        if ($this->actualDriver() === self::DRIVER_PREDIS) {
684 4
            $returned = $this->driver->sscan($key, $iterator, ['match' => $pattern, 'count' => $count]);
685 2
            $iterator = $returned[0];
686 2
            return $returned[1];
687 2
        }
688
        return $this->driver->sscan($key, $iterator, $pattern, $count);
689 2
    }
690
691
    /**
692
     * Returns null instead of false for Redis driver
693
     * @param mixed $result
694
     * @return mixed
695
     */
696
    private function convertFalseToNull($result)
697
    {
698 28
        return $this->actualDriver() === self::DRIVER_REDIS && $result === false ? null : $result;
699
    }
700 28
701 28
    /**
702 28
     * Transforms Predis result Payload to boolean
703
     * @param mixed $result
704
     * @return mixed
705
     */
706
    private function transformResult($result)
707
    {
708
        if ($this->actualDriver() === self::DRIVER_PREDIS && $result instanceof Status) {
709
            $result = $result->getPayload() === 'OK';
710
        }
711 12
        return $result;
712
    }
713 12
714 12
    /**
715 12
     * Create array from input array - odd keys are used as keys, even keys are used as values
716
     * @param array $dictionary
717
     * @param string $command
718
     * @return array
719
     * @throws RedisProxyException if number of keys is not the same as number of values
720
     */
721
    private function prepareKeyValue(array $dictionary, $command)
722
    {
723 4
        $keys = array_values(array_filter($dictionary, function ($key) {
724
            return $key % 2 == 0;
725 4
        }, ARRAY_FILTER_USE_KEY));
726 4
        $values = array_values(array_filter($dictionary, function ($key) {
727 4
            return $key % 2 == 1;
728
        }, ARRAY_FILTER_USE_KEY));
729
730
        if (count($keys) != count($values)) {
731
            throw new RedisProxyException("Wrong number of arguments for $command command");
732
        }
733
        return array_combine($keys, $values);
734
    }
735 4
736
    private function prepareArguments($command, ...$params)
737 4
    {
738 4
        if (!isset($params[0])) {
739 4
            throw new RedisProxyException("Wrong number of arguments for $command command");
740
        }
741
        if (is_array($params[0])) {
742
            $params = $params[0];
743
        }
744
        return $params;
745
    }
746
}
747