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

RedisProxy::getset()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 1

Importance

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