Completed
Pull Request — master (#5)
by Michal
02:20
created

RedisProxy::hscan()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 13
Code Lines 9

Duplication

Lines 13
Ratio 100 %

Code Coverage

Tests 9
CRAP Score 3

Importance

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