1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace RedisProxy; |
4
|
|
|
|
5
|
|
|
use Exception; |
6
|
|
|
use InvalidArgumentException; |
7
|
|
|
use Predis\Client; |
8
|
|
|
use Redis; |
9
|
|
|
|
10
|
|
|
class RedisProxy |
11
|
|
|
{ |
12
|
|
|
const DRIVER_REDIS = 'redis'; |
13
|
|
|
|
14
|
|
|
const DRIVER_PREDIS = 'predis'; |
15
|
|
|
|
16
|
|
|
private $driver; |
17
|
|
|
|
18
|
|
|
private $host; |
19
|
|
|
|
20
|
|
|
private $port; |
21
|
|
|
|
22
|
|
|
private $database = 0; |
23
|
|
|
|
24
|
|
|
private $timeout; |
25
|
|
|
|
26
|
|
|
private $supportedDrivers = [ |
27
|
|
|
self::DRIVER_REDIS, |
28
|
|
|
self::DRIVER_PREDIS, |
29
|
|
|
]; |
30
|
|
|
|
31
|
|
|
private $driversOrder = []; |
32
|
|
|
|
33
|
24 |
|
public function __construct($host, $port, $timeout = null) |
34
|
|
|
{ |
35
|
24 |
|
$this->host = $host; |
36
|
24 |
|
$this->port = $port; |
37
|
24 |
|
$this->timeout = $timeout; |
38
|
24 |
|
$this->driversOrder = $this->supportedDrivers; |
39
|
24 |
|
} |
40
|
|
|
|
41
|
24 |
|
public function setDriversOrder(array $driversOrder) |
42
|
|
|
{ |
43
|
24 |
|
foreach ($driversOrder as $driver) { |
44
|
24 |
|
if (!in_array($driver, $this->supportedDrivers)) { |
45
|
12 |
|
throw new InvalidArgumentException('Driver "' . $driver . '" is not supported'); |
46
|
|
|
} |
47
|
12 |
|
} |
48
|
24 |
|
$this->driversOrder = $driversOrder; |
49
|
24 |
|
return $this; |
50
|
|
|
} |
51
|
|
|
|
52
|
24 |
|
private function init() |
53
|
|
|
{ |
54
|
24 |
|
$this->prepareDriver(); |
55
|
24 |
|
$this->select($this->database); |
56
|
24 |
|
} |
57
|
|
|
|
58
|
24 |
|
private function prepareDriver() |
59
|
|
|
{ |
60
|
24 |
|
if ($this->driver !== null) { |
61
|
24 |
|
return; |
62
|
|
|
} |
63
|
|
|
|
64
|
24 |
|
foreach ($this->driversOrder as $preferredDriver) { |
65
|
24 |
|
if ($preferredDriver === self::DRIVER_REDIS && extension_loaded('redis')) { |
66
|
12 |
|
$this->driver = new Redis(); |
67
|
12 |
|
return; |
68
|
|
|
} |
69
|
12 |
|
if ($preferredDriver === self::DRIVER_PREDIS && class_exists('Predis\Client')) { |
70
|
12 |
|
$this->driver = new Client(); |
71
|
12 |
|
return; |
72
|
|
|
} |
73
|
|
|
} |
74
|
|
|
throw new RedisProxyException('No redis library loaded (ext-redis or predis)'); |
75
|
|
|
} |
76
|
|
|
|
77
|
24 |
|
private function connect($host, $port, $timeout = null) |
78
|
|
|
{ |
79
|
24 |
|
return $this->driver->connect($host, $port, $timeout); |
|
|
|
|
80
|
|
|
} |
81
|
|
|
|
82
|
24 |
|
private function isConnected() |
83
|
|
|
{ |
84
|
24 |
|
return $this->driver->isConnected(); |
|
|
|
|
85
|
|
|
} |
86
|
|
|
|
87
|
24 |
|
public function __call($name, $arguments) |
88
|
|
|
{ |
89
|
24 |
|
$this->init(); |
90
|
24 |
|
return call_user_func_array([$this->driver, $name], $arguments); |
91
|
|
|
} |
92
|
|
|
|
93
|
|
|
/** |
94
|
|
|
* @param string $database |
95
|
|
|
* @return type |
96
|
|
|
*/ |
97
|
24 |
|
public function select($database) |
98
|
|
|
{ |
99
|
24 |
|
$this->prepareDriver(); |
100
|
24 |
|
if (!$this->isConnected()) { |
101
|
24 |
|
$this->connect($this->host, $this->port, $this->timeout); |
102
|
12 |
|
} |
103
|
|
|
try { |
104
|
24 |
|
$result = $this->driver->select($database); |
|
|
|
|
105
|
13 |
|
} catch (Exception $e) { |
106
|
2 |
|
throw new RedisProxyException('Invalid DB index'); |
107
|
|
|
} |
108
|
24 |
|
if ($this->driver instanceof Client) { |
109
|
12 |
|
$result = $result->getPayload() === 'OK'; |
110
|
6 |
|
} |
111
|
24 |
|
if ($result === false) { |
112
|
2 |
|
throw new RedisProxyException('Invalid DB index'); |
113
|
|
|
} |
114
|
24 |
|
$this->database = $database; |
|
|
|
|
115
|
24 |
|
return $result; |
116
|
|
|
} |
117
|
|
|
|
118
|
|
|
/** |
119
|
|
|
* @param string $key |
120
|
|
|
* @return string |
121
|
|
|
*/ |
122
|
12 |
View Code Duplication |
public function get($key) |
|
|
|
|
123
|
|
|
{ |
124
|
12 |
|
$this->init(); |
125
|
12 |
|
$result = $this->driver->get($key); |
|
|
|
|
126
|
12 |
|
if ($this->driver instanceof Client) { |
127
|
6 |
|
$result = $result === null ? false : $result; |
128
|
3 |
|
} |
129
|
12 |
|
return $result; |
130
|
|
|
} |
131
|
|
|
|
132
|
|
|
/** |
133
|
|
|
* @param string $key |
134
|
|
|
* @param string $value |
135
|
|
|
* @return boolean |
136
|
|
|
*/ |
137
|
16 |
|
public function set($key, $value) |
138
|
|
|
{ |
139
|
16 |
|
$this->init(); |
140
|
16 |
|
$result = $this->driver->set($key, $value); |
|
|
|
|
141
|
16 |
|
if ($this->driver instanceof Client) { |
142
|
8 |
|
$result = $result->getPayload() === 'OK'; |
143
|
4 |
|
} |
144
|
16 |
|
return $result; |
145
|
|
|
} |
146
|
|
|
|
147
|
8 |
|
public function del(...$key) |
148
|
|
|
{ |
149
|
8 |
|
$this->init(); |
150
|
8 |
|
return $this->driver->del(...$key); |
|
|
|
|
151
|
|
|
} |
152
|
|
|
|
153
|
8 |
|
public function delete(...$key) |
154
|
|
|
{ |
155
|
8 |
|
return $this->del(...$key); |
156
|
|
|
} |
157
|
|
|
|
158
|
|
View Code Duplication |
public function scan(&$iterator, $pattern = null, $count = null) |
|
|
|
|
159
|
|
|
{ |
160
|
|
|
$this->init(); |
161
|
|
|
if ($this->driver instanceof Client) { |
162
|
|
|
$returned = $this->driver->scan($iterator, ['match' => $pattern, 'count' => $count]); |
163
|
|
|
$iterator = $returned[0]; |
164
|
|
|
return $returned[1]; |
165
|
|
|
} |
166
|
|
|
return $this->driver->scan($iterator, $pattern, $count); |
167
|
|
|
} |
168
|
|
|
|
169
|
4 |
View Code Duplication |
public function hget($key, $field) |
|
|
|
|
170
|
|
|
{ |
171
|
4 |
|
$this->init(); |
172
|
4 |
|
$result = $this->driver->hget($key, $field); |
|
|
|
|
173
|
4 |
|
if ($this->driver instanceof Client) { |
174
|
2 |
|
$result = $result === null ? false : $result; |
175
|
1 |
|
} |
176
|
4 |
|
return $result; |
177
|
|
|
} |
178
|
|
|
|
179
|
|
View Code Duplication |
public function hscan($key, &$iterator, $pattern = null, $count = null) |
|
|
|
|
180
|
|
|
{ |
181
|
|
|
$this->init(); |
182
|
|
|
if ($this->driver instanceof Client) { |
183
|
|
|
$returned = $this->driver->hscan($key, $iterator, ['match' => $pattern, 'count' => $count]); |
184
|
|
|
$iterator = $returned[0]; |
185
|
|
|
return $returned[1]; |
186
|
|
|
} |
187
|
|
|
return $this->driver->hscan($key, $iterator, $pattern, $count); |
188
|
|
|
} |
189
|
|
|
|
190
|
|
View Code Duplication |
public function zscan($key, &$iterator, $pattern = null, $count = null) |
|
|
|
|
191
|
|
|
{ |
192
|
|
|
$this->init(); |
193
|
|
|
if ($this->driver instanceof Client) { |
194
|
|
|
$returned = $this->driver->zscan($key, $iterator, ['match' => $pattern, 'count' => $count]); |
195
|
|
|
$iterator = $returned[0]; |
196
|
|
|
return $returned[1]; |
197
|
|
|
} |
198
|
|
|
return $this->driver->zscan($key, $iterator, $pattern, $count); |
199
|
|
|
} |
200
|
|
|
|
201
|
|
View Code Duplication |
public function sscan($key, &$iterator, $pattern = null, $count = null) |
|
|
|
|
202
|
|
|
{ |
203
|
|
|
$this->init(); |
204
|
|
|
if ($this->driver instanceof Client) { |
205
|
|
|
$returned = $this->driver->sscan($key, $iterator, ['match' => $pattern, 'count' => $count]); |
206
|
|
|
$iterator = $returned[0]; |
207
|
|
|
return $returned[1]; |
208
|
|
|
} |
209
|
|
|
return $this->driver->sscan($key, $iterator, $pattern, $count); |
210
|
|
|
} |
211
|
|
|
} |
212
|
|
|
|
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.