Completed
Push — master ( 968d28...159570 )
by Michal
01:22
created

RedisProxy::zrem()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

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