Completed
Pull Request — master (#1)
by Michal
02:00
created

RedisProxy::prepareDriver()   B

Complexity

Conditions 7
Paths 5

Size

Total Lines 18
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 7.2269

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 18
ccs 10
cts 12
cp 0.8333
rs 8.2222
cc 7
eloc 11
nc 5
nop 0
crap 7.2269
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 boolean set(string $key, string $value) Set the string value of a key
12
 * @method array mget(array $keys) Multi get - Returns the values of all specified keys. For every key that does not hold a string value or does not exist, FALSE is returned.
13
 * @method integer hset(string $key, string $field, string $value) Set the string value of a hash field
14
 * @method array hgetall(string $key) Get all fields and values in hash
15
 * @method array hGetAll(string $key) Get all fields and values in hash
16
 * @method integer hlen(string $key) Get the number of fields in hash
17
 * @method integer hLen(string $key) Get the number of fields in hash
18
 * @method boolean flushall() Remove all keys from all databases
19
 * @method boolean flushAll() Remove all keys from all databases
20
 * @method boolean flushdb() Remove all keys from the current database
21
 * @method boolean flushDb() Remove all keys from the current database
22
 */
23
class RedisProxy
24
{
25
    const DRIVER_REDIS = 'redis';
26
27
    const DRIVER_PREDIS = 'predis';
28
29
    private $driver;
30
31
    private $host;
32
33
    private $port;
34
35
    private $database = 0;
36
37
    private $timeout;
38
39
    private $supportedDrivers = [
40
        self::DRIVER_REDIS,
41
        self::DRIVER_PREDIS,
42
    ];
43
44
    private $driversOrder = [];
45
46 48
    public function __construct($host, $port, $timeout = null)
47
    {
48 48
        $this->host = $host;
49 48
        $this->port = $port;
50 48
        $this->timeout = $timeout;
51 48
        $this->driversOrder = $this->supportedDrivers;
52 48
    }
53
54 48
    public function setDriversOrder(array $driversOrder)
55
    {
56 48
        if (empty($driversOrder)) {
57 13
            throw new RedisProxyException('You need to set at least one driver');
58
        }
59 46
        foreach ($driversOrder as $driver) {
60 46
            if (!in_array($driver, $this->supportedDrivers)) {
61 24
                throw new RedisProxyException('Driver "' . $driver . '" is not supported');
62
            }
63 22
        }
64 44
        $this->driversOrder = $driversOrder;
65 44
        return $this;
66
    }
67
68 44
    private function init()
69
    {
70 44
        $this->prepareDriver();
71 44
        $this->select($this->database);
72 44
    }
73
74 44
    private function prepareDriver()
75
    {
76 44
        if ($this->driver !== null) {
77 44
            return;
78
        }
79
80 44
        foreach ($this->driversOrder as $preferredDriver) {
81 44
            if ($preferredDriver === self::DRIVER_REDIS && extension_loaded('redis')) {
82 22
                $this->driver = new Redis();
83 22
                return;
84
            }
85 22
            if ($preferredDriver === self::DRIVER_PREDIS && class_exists('Predis\Client')) {
86 22
                $this->driver = new Client();
87 22
                return;
88
            }
89
        }
90
        throw new RedisProxyException('No redis library loaded (ext-redis or predis)');
91
    }
92
93 44
    private function connect($host, $port, $timeout = null)
94
    {
95 44
        return $this->driver->connect($host, $port, $timeout);
1 ignored issue
show
Unused Code introduced by
The call to Client::connect() has too many arguments starting with $host.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
96
    }
97
98 44
    private function isConnected()
99
    {
100 44
        return $this->driver->isConnected();
1 ignored issue
show
Bug introduced by
The method isConnected does only exist in Predis\Client, but not in Redis.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
101
    }
102
103 44
    public function __call($name, $arguments)
104
    {
105 44
        $this->init();
106 44
        $result = call_user_func_array([$this->driver, $name], $arguments);
107 44
        if ($this->driver instanceof Client && $result instanceof Status) {
108 22
            $result = $result->getPayload() === 'OK';
109 11
        }
110 44
        return $result;
111
    }
112
113
    /**
114
     * @param integer $database
115
     * @return boolean true on success
116
     * @throws RedisProxyException on failure
117
     */
118 44
    public function select($database)
119
    {
120 44
        $this->prepareDriver();
121 44
        if (!$this->isConnected()) {
122 44
            $this->connect($this->host, $this->port, $this->timeout);
123 22
        }
124
        try {
125 44
            $result = $this->driver->select($database);
1 ignored issue
show
Bug introduced by
The method select does only exist in Redis, but not in Predis\Client.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
126 23
        } catch (Exception $e) {
127 2
            throw new RedisProxyException('Invalid DB index');
128
        }
129 44
        if ($this->driver instanceof Client) {
130 22
            $result = $result->getPayload() === 'OK';
131 11
        }
132 44
        if ($result === false) {
133 2
            throw new RedisProxyException('Invalid DB index');
134
        }
135 44
        $this->database = $database;
136 44
        return $result;
137
    }
138
139
    /**
140
     * @param string|null $section
141
     * @return array
142
     */
143 8
    public function info($section = null)
144
    {
145 8
        $this->init();
146 8
        if ($section === null) {
147 4
            $result = $this->driver->info();
1 ignored issue
show
Bug introduced by
The method info does only exist in Redis, but not in Predis\Client.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
148 2
        } else {
149 8
            $section = strtolower($section);
150 8
            $result = $this->driver->info($section);
151
        }
152
153 8
        $databases = $section === null || $section === 'keyspace' ? $this->config('get', 'databases')['databases'] : null;
0 ignored issues
show
Documentation Bug introduced by
The method config does not exist on object<RedisProxy\RedisProxy>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
154 8
        $groupedResult = InfoHelper::createInfoArray($this->driver, $result, $databases);
155 8
        if ($section === null) {
156 4
            return $groupedResult;
157
        }
158 8
        if (isset($groupedResult[$section])) {
159 4
            return $groupedResult[$section];
160
        }
161 4
        throw new RedisProxyException('Info section "' . $section . '" doesn\'t exist');
162
    }
163
164
    /**
165
     * @param string $key
166
     * @return string
167
     */
168 12 View Code Duplication
    public function get($key)
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...
169
    {
170 12
        $this->init();
171 12
        $result = $this->driver->get($key);
1 ignored issue
show
Bug introduced by
The method get does only exist in Redis, but not in Predis\Client.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
172 12
        if ($this->driver instanceof Client) {
173 6
            $result = $result === null ? false : $result;
174 3
        }
175 12
        return $result;
176
    }
177
178 12
    public function del(...$key)
179
    {
180 12
        $this->init();
181 12
        return $this->driver->del(...$key);
1 ignored issue
show
Bug introduced by
The method del does only exist in Redis, but not in Predis\Client.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
182
    }
183
184 8
    public function delete(...$key)
185
    {
186 8
        return $this->del(...$key);
187
    }
188
189 View Code Duplication
    public function scan(&$iterator, $pattern = null, $count = null)
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...
190
    {
191
        $this->init();
192
        if ($this->driver instanceof Client) {
193
            $returned = $this->driver->scan($iterator, ['match' => $pattern, 'count' => $count]);
194
            $iterator = $returned[0];
195
            return $returned[1];
196
        }
197
        return $this->driver->scan($iterator, $pattern, $count);
198
    }
199
200
    /**
201
     * Get the value of a hash field
202
     * @param string $key
203
     * @param string $field
204
     * @return string|boolean false if hash field is not set
205
     */
206 4 View Code Duplication
    public function hget($key, $field)
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...
207
    {
208 4
        $this->init();
209 4
        $result = $this->driver->hget($key, $field);
1 ignored issue
show
Bug introduced by
The method hget does only exist in Redis, but not in Predis\Client.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
210 4
        if ($this->driver instanceof Client) {
211 2
            $result = $result === null ? false : $result;
212 1
        }
213 4
        return $result;
214
    }
215
216
    /**
217
     * Delete one or more hash fields, returns number of deleted fields
218
     * @param string $key
219
     * @param array $fields
220
     * @return integer
221
     */
222 8
    public function hdel($key, ...$fields)
223
    {
224 8
        if (is_array($fields[0])) {
225 4
            $fields = $fields[0];
226 2
        }
227 8
        $res = $this->driver->hdel($key, ...$fields);
1 ignored issue
show
Bug introduced by
The method hdel does only exist in Redis, but not in Predis\Client.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
228
229 8
        return $res;
230
    }
231
232 View Code Duplication
    public function hscan($key, &$iterator, $pattern = null, $count = null)
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...
233
    {
234
        $this->init();
235
        if ($this->driver instanceof Client) {
236
            $returned = $this->driver->hscan($key, $iterator, ['match' => $pattern, 'count' => $count]);
237
            $iterator = $returned[0];
238
            return $returned[1];
239
        }
240
        return $this->driver->hscan($key, $iterator, $pattern, $count);
241
    }
242
243 View Code Duplication
    public function zscan($key, &$iterator, $pattern = null, $count = null)
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...
244
    {
245
        $this->init();
246
        if ($this->driver instanceof Client) {
247
            $returned = $this->driver->zscan($key, $iterator, ['match' => $pattern, 'count' => $count]);
248
            $iterator = $returned[0];
249
            return $returned[1];
250
        }
251
        return $this->driver->zscan($key, $iterator, $pattern, $count);
252
    }
253
254 View Code Duplication
    public function sscan($key, &$iterator, $pattern = null, $count = null)
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...
255
    {
256
        $this->init();
257
        if ($this->driver instanceof Client) {
258
            $returned = $this->driver->sscan($key, $iterator, ['match' => $pattern, 'count' => $count]);
259
            $iterator = $returned[0];
260
            return $returned[1];
261
        }
262
        return $this->driver->sscan($key, $iterator, $pattern, $count);
263
    }
264
}
265