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

RedisProxy::hincrbyfloat()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

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