Version Notes
1.8.0.0
Download this release
Release Info
Developer | Magento Core Team |
Extension | Lib_Credis |
Version | 1.8.0.0 |
Comparing to | |
See all releases |
Version 1.8.0.0
- lib/Credis/Client.php +793 -0
- package.xml +18 -0
lib/Credis/Client.php
ADDED
@@ -0,0 +1,793 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Credis_Client (a fork of Redisent)
|
4 |
+
*
|
5 |
+
* Most commands are compatible with phpredis library:
|
6 |
+
* - use "pipeline()" to start a pipeline of commands instead of multi(Redis::PIPELINE)
|
7 |
+
* - any arrays passed as arguments will be flattened automatically
|
8 |
+
* - setOption and getOption are not supported in standalone mode
|
9 |
+
* - order of arguments follows redis-cli instead of phpredis where they differ (lrem)
|
10 |
+
*
|
11 |
+
* - Uses phpredis library if extension is installed for better performance.
|
12 |
+
* - Establishes connection lazily.
|
13 |
+
* - Supports tcp and unix sockets.
|
14 |
+
* - Reconnects automatically unless a watch or transaction is in progress.
|
15 |
+
* - Can set automatic retry connection attempts for iffy Redis connections.
|
16 |
+
*
|
17 |
+
* @author Colin Mollenhour <colin@mollenhour.com>
|
18 |
+
* @copyright 2011 Colin Mollenhour <colin@mollenhour.com>
|
19 |
+
* @license http://www.opensource.org/licenses/mit-license.php The MIT License
|
20 |
+
* @package Credis_Client
|
21 |
+
*/
|
22 |
+
|
23 |
+
if( ! defined('CRLF')) define('CRLF', sprintf('%s%s', chr(13), chr(10)));
|
24 |
+
|
25 |
+
/**
|
26 |
+
* Credis-specific errors, wraps native Redis errors
|
27 |
+
*/
|
28 |
+
class CredisException extends Exception {
|
29 |
+
}
|
30 |
+
|
31 |
+
/**
|
32 |
+
* Credis_Client, a lightweight Redis PHP standalone client and phpredis wrapper
|
33 |
+
*
|
34 |
+
* Server/Connection:
|
35 |
+
* @method Credis_Client pipeline()
|
36 |
+
* @method Credis_Client multi()
|
37 |
+
* @method array exec()
|
38 |
+
* @method string flushAll()
|
39 |
+
* @method string flushDb()
|
40 |
+
* @method array info()
|
41 |
+
* @method bool|array config(string $setGet, string $key, string $value = null)
|
42 |
+
*
|
43 |
+
* Keys:
|
44 |
+
* @method int del(string $key)
|
45 |
+
* @method int exists(string $key)
|
46 |
+
* @method int expire(string $key, int $seconds)
|
47 |
+
* @method int expireAt(string $key, int $timestamp)
|
48 |
+
* @method array keys(string $key)
|
49 |
+
* @method int persist(string $key)
|
50 |
+
* @method bool rename(string $key, string $newKey)
|
51 |
+
* @method bool renameNx(string $key, string $newKey)
|
52 |
+
* @method array sort(string $key, string $arg1, string $valueN = null)
|
53 |
+
* @method int ttl(string $key)
|
54 |
+
* @method string type(string $key)
|
55 |
+
*
|
56 |
+
* Scalars:
|
57 |
+
* @method int append(string $key, string $value)
|
58 |
+
* @method int decr(string $key)
|
59 |
+
* @method int decrBy(string $key, int $decrement)
|
60 |
+
* @method bool|string get(string $key)
|
61 |
+
* @method int getBit(string $key, int $offset)
|
62 |
+
* @method string getRange(string $key, int $start, int $end)
|
63 |
+
* @method string getSet(string $key, string $value)
|
64 |
+
* @method int incr(string $key)
|
65 |
+
* @method int incrBy(string $key, int $decrement)
|
66 |
+
* @method array mGet(array $keys)
|
67 |
+
* @method bool mSet(array $keysValues)
|
68 |
+
* @method int mSetNx(array $keysValues)
|
69 |
+
* @method bool set(string $key, string $value)
|
70 |
+
* @method int setBit(string $key, int $offset, int $value)
|
71 |
+
* @method bool setEx(string $key, int $seconds, string $value)
|
72 |
+
* @method int setNx(string $key, string $value)
|
73 |
+
* @method int setRange(string $key, int $offset, int $value)
|
74 |
+
* @method int strLen(string $key)
|
75 |
+
*
|
76 |
+
* Sets:
|
77 |
+
* @method int sAdd(string $key, mixed $value, string $valueN = null)
|
78 |
+
* @method int sRem(string $key, mixed $value, string $valueN = null)
|
79 |
+
* @method array sMembers(string $key)
|
80 |
+
* @method array sUnion(mixed $keyOrArray, string $valueN = null)
|
81 |
+
* @method array sInter(mixed $keyOrArray, string $valueN = null)
|
82 |
+
* @method array sDiff(mixed $keyOrArray, string $valueN = null)
|
83 |
+
* @method string sPop(string $key)
|
84 |
+
* @method int sCard(string $key)
|
85 |
+
* @method int sIsMember(string $key, string $member)
|
86 |
+
* @method int sMove(string $source, string $dest, string $member)
|
87 |
+
* @method string|array sRandMember(string $key, int $count = null)
|
88 |
+
* @method int sUnionStore(string $dest, string $key1, string $key2 = null)
|
89 |
+
* @method int sInterStore(string $dest, string $key1, string $key2 = null)
|
90 |
+
* @method int sDiffStore(string $dest, string $key1, string $key2 = null)
|
91 |
+
*
|
92 |
+
* Hashes:
|
93 |
+
* @method bool|int hSet(string $key, string $field, string $value)
|
94 |
+
* @method bool hSetNx(string $key, string $field, string $value)
|
95 |
+
* @method bool|string hGet(string $key, string $field)
|
96 |
+
* @method bool|int hLen(string $key)
|
97 |
+
* @method bool hDel(string $key, string $field)
|
98 |
+
* @method array hKeys(string $key, string $field)
|
99 |
+
* @method array hVals(string $key, string $field)
|
100 |
+
* @method array hGetAll(string $key)
|
101 |
+
* @method bool hExists(string $key, string $field)
|
102 |
+
* @method int hIncrBy(string $key, string $field, int $value)
|
103 |
+
* @method bool hMSet(string $key, array $keysValues)
|
104 |
+
* @method array hMGet(string $key, array $fields)
|
105 |
+
*
|
106 |
+
* Lists:
|
107 |
+
* @method array|null blPop(string $keyN, int $timeout)
|
108 |
+
* @method array|null brPop(string $keyN, int $timeout)
|
109 |
+
* @method array|null brPoplPush(string $source, string $destination, int $timeout)
|
110 |
+
* @method string|null lIndex(string $key, int $index)
|
111 |
+
* @method int lInsert(string $key, string $beforeAfter, string $pivot, string $value)
|
112 |
+
* @method int lLen(string $key)
|
113 |
+
* @method string|null lPop(string $key)
|
114 |
+
* @method int lPush(string $key, mixed $value, mixed $valueN = null)
|
115 |
+
* @method int lPushX(string $key, mixed $value)
|
116 |
+
* @method array lRange(string $key, int $start, int $stop)
|
117 |
+
* @method int lRem(string $key, int $count, mixed $value)
|
118 |
+
* @method bool lSet(string $key, int $index, mixed $value)
|
119 |
+
* @method bool lTrim(string $key, int $start, int $stop)
|
120 |
+
* @method string|null rPop(string $key)
|
121 |
+
* @method string|null rPoplPush(string $source, string $destination)
|
122 |
+
* @method int rPush(string $key, mixed $value, mixed $valueN = null)
|
123 |
+
* @method int rPushX(string $key, mixed $value)
|
124 |
+
*
|
125 |
+
* Sorted Sets:
|
126 |
+
* TODO
|
127 |
+
*
|
128 |
+
* Pub/Sub
|
129 |
+
* TODO
|
130 |
+
*
|
131 |
+
* Scripting:
|
132 |
+
* @method string|int script(string $command, string $arg1 = null)
|
133 |
+
* @method string|int|array|bool eval(string $script, int $numkeys, string $key = null, string $arg = null)
|
134 |
+
* @method string|int|array|bool evalSha(string $sha1, int $numkeys, string $key = null, string $arg = null)
|
135 |
+
*/
|
136 |
+
class Credis_Client {
|
137 |
+
|
138 |
+
const TYPE_STRING = 'string';
|
139 |
+
const TYPE_LIST = 'list';
|
140 |
+
const TYPE_SET = 'set';
|
141 |
+
const TYPE_ZSET = 'zset';
|
142 |
+
const TYPE_HASH = 'hash';
|
143 |
+
const TYPE_NONE = 'none';
|
144 |
+
const FREAD_BLOCK_SIZE = 8192;
|
145 |
+
|
146 |
+
/**
|
147 |
+
* Socket connection to the Redis server or Redis library instance
|
148 |
+
* @var resource|Redis
|
149 |
+
*/
|
150 |
+
protected $redis;
|
151 |
+
protected $redisMulti;
|
152 |
+
|
153 |
+
/**
|
154 |
+
* Host of the Redis server
|
155 |
+
* @var string
|
156 |
+
*/
|
157 |
+
protected $host;
|
158 |
+
|
159 |
+
/**
|
160 |
+
* Port on which the Redis server is running
|
161 |
+
* @var integer
|
162 |
+
*/
|
163 |
+
protected $port;
|
164 |
+
|
165 |
+
/**
|
166 |
+
* Timeout for connecting to Redis server
|
167 |
+
* @var float
|
168 |
+
*/
|
169 |
+
protected $timeout;
|
170 |
+
|
171 |
+
/**
|
172 |
+
* Timeout for reading response from Redis server
|
173 |
+
* @var float
|
174 |
+
*/
|
175 |
+
protected $readTimeout;
|
176 |
+
|
177 |
+
/**
|
178 |
+
* Unique identifier for persistent connections
|
179 |
+
* @var string
|
180 |
+
*/
|
181 |
+
protected $persistent;
|
182 |
+
|
183 |
+
/**
|
184 |
+
* @var bool
|
185 |
+
*/
|
186 |
+
protected $closeOnDestruct = TRUE;
|
187 |
+
|
188 |
+
/**
|
189 |
+
* @var bool
|
190 |
+
*/
|
191 |
+
protected $connected = FALSE;
|
192 |
+
|
193 |
+
/**
|
194 |
+
* @var bool
|
195 |
+
*/
|
196 |
+
protected $standalone;
|
197 |
+
|
198 |
+
/**
|
199 |
+
* @var int
|
200 |
+
*/
|
201 |
+
protected $maxConnectRetries = 0;
|
202 |
+
|
203 |
+
/**
|
204 |
+
* @var int
|
205 |
+
*/
|
206 |
+
protected $connectFailures = 0;
|
207 |
+
|
208 |
+
/**
|
209 |
+
* @var bool
|
210 |
+
*/
|
211 |
+
protected $usePipeline = FALSE;
|
212 |
+
|
213 |
+
/**
|
214 |
+
* @var array
|
215 |
+
*/
|
216 |
+
protected $commandNames;
|
217 |
+
|
218 |
+
/**
|
219 |
+
* @var string
|
220 |
+
*/
|
221 |
+
protected $commands;
|
222 |
+
|
223 |
+
/**
|
224 |
+
* @var bool
|
225 |
+
*/
|
226 |
+
protected $isMulti = FALSE;
|
227 |
+
|
228 |
+
/**
|
229 |
+
* @var bool
|
230 |
+
*/
|
231 |
+
protected $isWatching = FALSE;
|
232 |
+
|
233 |
+
/**
|
234 |
+
* @var string
|
235 |
+
*/
|
236 |
+
protected $authPassword;
|
237 |
+
|
238 |
+
/**
|
239 |
+
* @var int
|
240 |
+
*/
|
241 |
+
protected $selectedDb = 0;
|
242 |
+
|
243 |
+
/**
|
244 |
+
* Aliases for backwards compatibility with phpredis
|
245 |
+
* @var array
|
246 |
+
*/
|
247 |
+
protected $aliasedMethods = array('delete' => 'del', 'getkeys' => 'keys', 'sremove' => 'srem');
|
248 |
+
|
249 |
+
/**
|
250 |
+
* Creates a Redisent connection to the Redis server on host {@link $host} and port {@link $port}.
|
251 |
+
* $host may also be a path to a unix socket or a string in the form of tcp://[hostname]:[port] or unix://[path]
|
252 |
+
*
|
253 |
+
* @param string $host The hostname of the Redis server
|
254 |
+
* @param integer $port The port number of the Redis server
|
255 |
+
* @param float $timeout Timeout period in seconds
|
256 |
+
* @param string $persistent Flag to establish persistent connection
|
257 |
+
*/
|
258 |
+
public function __construct($host = '127.0.0.1', $port = 6379, $timeout = null, $persistent = '')
|
259 |
+
{
|
260 |
+
$this->host = (string) $host;
|
261 |
+
$this->port = (int) $port;
|
262 |
+
$this->timeout = $timeout;
|
263 |
+
$this->persistent = (string) $persistent;
|
264 |
+
$this->standalone = ! extension_loaded('redis');
|
265 |
+
}
|
266 |
+
|
267 |
+
public function __destruct()
|
268 |
+
{
|
269 |
+
if ($this->closeOnDestruct) {
|
270 |
+
$this->close();
|
271 |
+
}
|
272 |
+
}
|
273 |
+
|
274 |
+
/**
|
275 |
+
* @throws CredisException
|
276 |
+
* @return Credis_Client
|
277 |
+
*/
|
278 |
+
public function forceStandalone()
|
279 |
+
{
|
280 |
+
if($this->connected) {
|
281 |
+
throw new CredisException('Cannot force Credis_Client to use standalone PHP driver after a connection has already been established.');
|
282 |
+
}
|
283 |
+
$this->standalone = TRUE;
|
284 |
+
return $this;
|
285 |
+
}
|
286 |
+
|
287 |
+
/**
|
288 |
+
* @param int $retries
|
289 |
+
* @return Credis_Client
|
290 |
+
*/
|
291 |
+
public function setMaxConnectRetries($retries)
|
292 |
+
{
|
293 |
+
$this->maxConnectRetries = $retries;
|
294 |
+
return $this;
|
295 |
+
}
|
296 |
+
|
297 |
+
/**
|
298 |
+
* @param bool $flag
|
299 |
+
* @return Credis_Client
|
300 |
+
*/
|
301 |
+
public function setCloseOnDestruct($flag)
|
302 |
+
{
|
303 |
+
$this->closeOnDestruct = $flag;
|
304 |
+
return $this;
|
305 |
+
}
|
306 |
+
|
307 |
+
/**
|
308 |
+
* @throws CredisException
|
309 |
+
* @return Credis_Client
|
310 |
+
*/
|
311 |
+
public function connect()
|
312 |
+
{
|
313 |
+
if ($this->connected) {
|
314 |
+
return $this;
|
315 |
+
}
|
316 |
+
if (preg_match('#^(tcp|unix)://(.*)$#', $this->host, $matches)) {
|
317 |
+
if($matches[1] == 'tcp') {
|
318 |
+
if ( ! preg_match('#^(.*)(?::(\d+))?(?:/(.*))?$#', $matches[2], $matches)) {
|
319 |
+
throw new CredisException('Invalid host format; expected tcp://host[:port][/persistent]');
|
320 |
+
}
|
321 |
+
$this->host = $matches[1];
|
322 |
+
$this->port = (int) (isset($matches[2]) ? $matches[2] : 6379);
|
323 |
+
$this->persistent = isset($matches[3]) ? $matches[3] : '';
|
324 |
+
} else {
|
325 |
+
$this->host = $matches[2];
|
326 |
+
$this->port = NULL;
|
327 |
+
if (substr($this->host,0,1) != '/') {
|
328 |
+
throw new CredisException('Invalid unix socket format; expected unix:///path/to/redis.sock');
|
329 |
+
}
|
330 |
+
}
|
331 |
+
}
|
332 |
+
if ($this->port !== NULL && substr($this->host,0,1) == '/') {
|
333 |
+
$this->port = NULL;
|
334 |
+
}
|
335 |
+
if ($this->standalone) {
|
336 |
+
$flags = STREAM_CLIENT_CONNECT;
|
337 |
+
$remote_socket = $this->port === NULL
|
338 |
+
? 'unix://'.$this->host
|
339 |
+
: 'tcp://'.$this->host.':'.$this->port;
|
340 |
+
if ($this->persistent) {
|
341 |
+
if ($this->port === NULL) { // Unix socket
|
342 |
+
throw new CredisException('Persistent connections to UNIX sockets are not supported in standalone mode.');
|
343 |
+
}
|
344 |
+
$remote_socket .= '/'.$this->persistent;
|
345 |
+
$flags = $flags | STREAM_CLIENT_PERSISTENT;
|
346 |
+
}
|
347 |
+
$result = $this->redis = @stream_socket_client($remote_socket, $errno, $errstr, $this->timeout !== null ? $this->timeout : 2.5, $flags);
|
348 |
+
}
|
349 |
+
else {
|
350 |
+
if ( ! $this->redis) {
|
351 |
+
$this->redis = new Redis;
|
352 |
+
}
|
353 |
+
$result = $this->persistent
|
354 |
+
? $this->redis->pconnect($this->host, $this->port, $this->timeout, $this->persistent)
|
355 |
+
: $this->redis->connect($this->host, $this->port, $this->timeout);
|
356 |
+
}
|
357 |
+
|
358 |
+
// Use recursion for connection retries
|
359 |
+
if ( ! $result) {
|
360 |
+
$this->connectFailures++;
|
361 |
+
if ($this->connectFailures <= $this->maxConnectRetries) {
|
362 |
+
return $this->connect();
|
363 |
+
}
|
364 |
+
$failures = $this->connectFailures;
|
365 |
+
$this->connectFailures = 0;
|
366 |
+
throw new CredisException("Connection to Redis failed after $failures failures.");
|
367 |
+
}
|
368 |
+
|
369 |
+
$this->connectFailures = 0;
|
370 |
+
$this->connected = TRUE;
|
371 |
+
|
372 |
+
// Set read timeout
|
373 |
+
if ($this->readTimeout) {
|
374 |
+
$this->setReadTimeout($this->readTimeout);
|
375 |
+
}
|
376 |
+
|
377 |
+
return $this;
|
378 |
+
}
|
379 |
+
|
380 |
+
/**
|
381 |
+
* Set the read timeout for the connection. If falsey, a timeout will not be set. Negative values not supported.
|
382 |
+
*
|
383 |
+
* @param $timeout
|
384 |
+
* @throws CredisException
|
385 |
+
* @return Credis_Client
|
386 |
+
*/
|
387 |
+
public function setReadTimeout($timeout)
|
388 |
+
{
|
389 |
+
if ($timeout < 0) {
|
390 |
+
throw new CredisException('Negative read timeout values are not supported.');
|
391 |
+
}
|
392 |
+
$this->readTimeout = $timeout;
|
393 |
+
if ($this->connected) {
|
394 |
+
if ($this->standalone) {
|
395 |
+
stream_set_timeout($this->redis, (int) floor($timeout), ($timeout - floor($timeout)) * 1000000);
|
396 |
+
} else if (defined('Redis::OPT_READ_TIMEOUT')) {
|
397 |
+
// Not supported at time of writing, but hopefully this pull request will someday be merged:
|
398 |
+
// https://github.com/nicolasff/phpredis/pull/260
|
399 |
+
$this->redis->setOption(Redis::OPT_READ_TIMEOUT, $timeout);
|
400 |
+
}
|
401 |
+
}
|
402 |
+
return $this;
|
403 |
+
}
|
404 |
+
|
405 |
+
/**
|
406 |
+
* @return bool
|
407 |
+
*/
|
408 |
+
public function close()
|
409 |
+
{
|
410 |
+
$result = TRUE;
|
411 |
+
if ($this->connected && ! $this->persistent) {
|
412 |
+
try {
|
413 |
+
$result = $this->standalone ? fclose($this->redis) : $this->redis->close();
|
414 |
+
$this->connected = FALSE;
|
415 |
+
} catch (Exception $e) {
|
416 |
+
; // Ignore exceptions on close
|
417 |
+
}
|
418 |
+
}
|
419 |
+
return $result;
|
420 |
+
}
|
421 |
+
|
422 |
+
/**
|
423 |
+
* @param string $password
|
424 |
+
* @return bool
|
425 |
+
*/
|
426 |
+
public function auth($password)
|
427 |
+
{
|
428 |
+
$this->authPassword = $password;
|
429 |
+
$response = $this->__call('auth', array($this->authPassword));
|
430 |
+
return $response;
|
431 |
+
}
|
432 |
+
|
433 |
+
/**
|
434 |
+
* @param int $index
|
435 |
+
* @return bool
|
436 |
+
*/
|
437 |
+
public function select($index)
|
438 |
+
{
|
439 |
+
$this->selectedDb = (int) $index;
|
440 |
+
$response = $this->__call('select', array($this->selectedDb));
|
441 |
+
return $response;
|
442 |
+
}
|
443 |
+
|
444 |
+
public function __call($name, $args)
|
445 |
+
{
|
446 |
+
// Lazy connection
|
447 |
+
$this->connect();
|
448 |
+
|
449 |
+
$name = strtolower($name);
|
450 |
+
|
451 |
+
// Send request via native PHP
|
452 |
+
if($this->standalone)
|
453 |
+
{
|
454 |
+
// Flatten arguments
|
455 |
+
$argsFlat = NULL;
|
456 |
+
foreach($args as $index => $arg) {
|
457 |
+
if(is_array($arg)) {
|
458 |
+
if($argsFlat === NULL) {
|
459 |
+
$argsFlat = array_slice($args, 0, $index);
|
460 |
+
}
|
461 |
+
if($name == 'mset' || $name == 'msetnx' || $name == 'hmset') {
|
462 |
+
foreach($arg as $key => $value) {
|
463 |
+
$argsFlat[] = $key;
|
464 |
+
$argsFlat[] = $value;
|
465 |
+
}
|
466 |
+
} else {
|
467 |
+
$argsFlat = array_merge($argsFlat, $arg);
|
468 |
+
}
|
469 |
+
} else if($argsFlat !== NULL) {
|
470 |
+
$argsFlat[] = $arg;
|
471 |
+
}
|
472 |
+
}
|
473 |
+
if($argsFlat !== NULL) {
|
474 |
+
$args = $argsFlat;
|
475 |
+
$argsFlat = NULL;
|
476 |
+
}
|
477 |
+
|
478 |
+
// In pipeline mode
|
479 |
+
if($this->usePipeline)
|
480 |
+
{
|
481 |
+
if($name == 'pipeline') {
|
482 |
+
throw new CredisException('A pipeline is already in use and only one pipeline is supported.');
|
483 |
+
}
|
484 |
+
else if($name == 'exec') {
|
485 |
+
if($this->isMulti) {
|
486 |
+
$this->commandNames[] = $name;
|
487 |
+
$this->commands .= self::_prepare_command(array($name));
|
488 |
+
}
|
489 |
+
|
490 |
+
// Write request
|
491 |
+
if($this->commands) {
|
492 |
+
$this->write_command($this->commands);
|
493 |
+
}
|
494 |
+
$this->commands = NULL;
|
495 |
+
|
496 |
+
// Read response
|
497 |
+
$response = array();
|
498 |
+
foreach($this->commandNames as $command) {
|
499 |
+
$response[] = $this->read_reply($command);
|
500 |
+
}
|
501 |
+
$this->commandNames = NULL;
|
502 |
+
|
503 |
+
if($this->isMulti) {
|
504 |
+
$response = array_pop($response);
|
505 |
+
}
|
506 |
+
$this->usePipeline = $this->isMulti = FALSE;
|
507 |
+
return $response;
|
508 |
+
}
|
509 |
+
else {
|
510 |
+
if($name == 'multi') {
|
511 |
+
$this->isMulti = TRUE;
|
512 |
+
}
|
513 |
+
array_unshift($args, $name);
|
514 |
+
$this->commandNames[] = $name;
|
515 |
+
$this->commands .= self::_prepare_command($args);
|
516 |
+
return $this;
|
517 |
+
}
|
518 |
+
}
|
519 |
+
|
520 |
+
// Start pipeline mode
|
521 |
+
if($name == 'pipeline')
|
522 |
+
{
|
523 |
+
$this->usePipeline = TRUE;
|
524 |
+
$this->commandNames = array();
|
525 |
+
$this->commands = '';
|
526 |
+
return $this;
|
527 |
+
}
|
528 |
+
|
529 |
+
// If unwatching, allow reconnect with no error thrown
|
530 |
+
if($name == 'unwatch') {
|
531 |
+
$this->isWatching = FALSE;
|
532 |
+
}
|
533 |
+
|
534 |
+
// Non-pipeline mode
|
535 |
+
array_unshift($args, $name);
|
536 |
+
$command = self::_prepare_command($args);
|
537 |
+
$this->write_command($command);
|
538 |
+
$response = $this->read_reply($name);
|
539 |
+
|
540 |
+
// Watch mode disables reconnect so error is thrown
|
541 |
+
if($name == 'watch') {
|
542 |
+
$this->isWatching = TRUE;
|
543 |
+
}
|
544 |
+
// Transaction mode
|
545 |
+
else if($this->isMulti && ($name == 'exec' || $name == 'discard')) {
|
546 |
+
$this->isMulti = FALSE;
|
547 |
+
}
|
548 |
+
// Started transaction
|
549 |
+
else if($this->isMulti || $name == 'multi') {
|
550 |
+
$this->isMulti = TRUE;
|
551 |
+
$response = $this;
|
552 |
+
}
|
553 |
+
}
|
554 |
+
|
555 |
+
// Send request via phpredis client
|
556 |
+
else
|
557 |
+
{
|
558 |
+
// Tweak arguments
|
559 |
+
switch($name) {
|
560 |
+
case 'get': // optimize common cases
|
561 |
+
case 'set':
|
562 |
+
case 'hget':
|
563 |
+
case 'hset':
|
564 |
+
case 'setex':
|
565 |
+
case 'mset':
|
566 |
+
case 'msetnx':
|
567 |
+
case 'hmset':
|
568 |
+
case 'hmget':
|
569 |
+
case 'del':
|
570 |
+
break;
|
571 |
+
case 'mget':
|
572 |
+
if(isset($args[0]) && ! is_array($args[0])) {
|
573 |
+
$args = array($args);
|
574 |
+
}
|
575 |
+
break;
|
576 |
+
case 'lrem':
|
577 |
+
$args = array($args[0], $args[2], $args[1]);
|
578 |
+
break;
|
579 |
+
default:
|
580 |
+
// Flatten arguments
|
581 |
+
$argsFlat = NULL;
|
582 |
+
foreach($args as $index => $arg) {
|
583 |
+
if(is_array($arg)) {
|
584 |
+
if($argsFlat === NULL) {
|
585 |
+
$argsFlat = array_slice($args, 0, $index);
|
586 |
+
}
|
587 |
+
$argsFlat = array_merge($argsFlat, $arg);
|
588 |
+
} else if($argsFlat !== NULL) {
|
589 |
+
$argsFlat[] = $arg;
|
590 |
+
}
|
591 |
+
}
|
592 |
+
if($argsFlat !== NULL) {
|
593 |
+
$args = $argsFlat;
|
594 |
+
$argsFlat = NULL;
|
595 |
+
}
|
596 |
+
}
|
597 |
+
|
598 |
+
try {
|
599 |
+
// Proxy pipeline mode to the phpredis library
|
600 |
+
if($name == 'pipeline' || $name == 'multi') {
|
601 |
+
if($this->isMulti) {
|
602 |
+
return $this;
|
603 |
+
} else {
|
604 |
+
$this->isMulti = TRUE;
|
605 |
+
$this->redisMulti = call_user_func_array(array($this->redis, $name), $args);
|
606 |
+
}
|
607 |
+
}
|
608 |
+
else if($name == 'exec' || $name == 'discard') {
|
609 |
+
$this->isMulti = FALSE;
|
610 |
+
$response = $this->redisMulti->$name();
|
611 |
+
$this->redisMulti = NULL;
|
612 |
+
#echo "> $name : ".substr(print_r($response, TRUE),0,100)."\n";
|
613 |
+
return $response;
|
614 |
+
}
|
615 |
+
|
616 |
+
// Use aliases to be compatible with phpredis wrapper
|
617 |
+
if(isset($this->aliasedMethods[$name])) {
|
618 |
+
$name = $this->aliasedMethods[$name];
|
619 |
+
}
|
620 |
+
|
621 |
+
// Multi and pipeline return self for chaining
|
622 |
+
if($this->isMulti) {
|
623 |
+
call_user_func_array(array($this->redisMulti, $name), $args);
|
624 |
+
return $this;
|
625 |
+
}
|
626 |
+
|
627 |
+
$response = call_user_func_array(array($this->redis, $name), $args);
|
628 |
+
}
|
629 |
+
// Wrap exceptions
|
630 |
+
catch(RedisException $e) {
|
631 |
+
throw new CredisException($e->getMessage(), $e->getCode());
|
632 |
+
}
|
633 |
+
|
634 |
+
#echo "> $name : ".substr(print_r($response, TRUE),0,100)."\n";
|
635 |
+
|
636 |
+
// change return values where it is too difficult to minim in standalone mode
|
637 |
+
switch($name)
|
638 |
+
{
|
639 |
+
case 'hmget':
|
640 |
+
$response = array_values($response);
|
641 |
+
break;
|
642 |
+
case 'type':
|
643 |
+
$typeMap = array(
|
644 |
+
self::TYPE_NONE,
|
645 |
+
self::TYPE_STRING,
|
646 |
+
self::TYPE_SET,
|
647 |
+
self::TYPE_LIST,
|
648 |
+
self::TYPE_ZSET,
|
649 |
+
self::TYPE_HASH,
|
650 |
+
);
|
651 |
+
$response = $typeMap[$response];
|
652 |
+
break;
|
653 |
+
}
|
654 |
+
}
|
655 |
+
|
656 |
+
return $response;
|
657 |
+
}
|
658 |
+
|
659 |
+
protected function write_command($command)
|
660 |
+
{
|
661 |
+
// Reconnect on lost connection (Redis server "timeout" exceeded since last command)
|
662 |
+
if(feof($this->redis)) {
|
663 |
+
$this->close();
|
664 |
+
// If a watch or transaction was in progress and connection was lost, throw error rather than reconnect
|
665 |
+
// since transaction/watch state will be lost.
|
666 |
+
if(($this->isMulti && ! $this->usePipeline) || $this->isWatching) {
|
667 |
+
$this->isMulti = $this->isWatching = FALSE;
|
668 |
+
throw new CredisException('Lost connection to Redis server during watch or transaction.');
|
669 |
+
}
|
670 |
+
$this->connected = FALSE;
|
671 |
+
$this->connect();
|
672 |
+
if($this->authPassword) {
|
673 |
+
$this->auth($this->authPassword);
|
674 |
+
}
|
675 |
+
if($this->selectedDb != 0) {
|
676 |
+
$this->select($this->selectedDb);
|
677 |
+
}
|
678 |
+
}
|
679 |
+
|
680 |
+
$commandLen = strlen($command);
|
681 |
+
for ($written = 0; $written < $commandLen; $written += $fwrite) {
|
682 |
+
$fwrite = fwrite($this->redis, substr($command, $written));
|
683 |
+
if ($fwrite === FALSE || $fwrite == 0 ) {
|
684 |
+
throw new CredisException('Failed to write entire command to stream');
|
685 |
+
}
|
686 |
+
}
|
687 |
+
}
|
688 |
+
|
689 |
+
protected function read_reply($name = '')
|
690 |
+
{
|
691 |
+
$reply = fgets($this->redis);
|
692 |
+
if($reply === FALSE) {
|
693 |
+
throw new CredisException('Lost connection to Redis server.');
|
694 |
+
}
|
695 |
+
$reply = rtrim($reply, CRLF);
|
696 |
+
#echo "> $name: $reply\n";
|
697 |
+
$replyType = substr($reply, 0, 1);
|
698 |
+
switch ($replyType) {
|
699 |
+
/* Error reply */
|
700 |
+
case '-':
|
701 |
+
if($this->isMulti || $this->usePipeline) {
|
702 |
+
$response = FALSE;
|
703 |
+
} else {
|
704 |
+
throw new CredisException(substr($reply, 4));
|
705 |
+
}
|
706 |
+
break;
|
707 |
+
/* Inline reply */
|
708 |
+
case '+':
|
709 |
+
$response = substr($reply, 1);
|
710 |
+
if($response == 'OK' || $response == 'QUEUED') {
|
711 |
+
return TRUE;
|
712 |
+
}
|
713 |
+
break;
|
714 |
+
/* Bulk reply */
|
715 |
+
case '$':
|
716 |
+
if ($reply == '$-1') return FALSE;
|
717 |
+
$size = (int) substr($reply, 1);
|
718 |
+
$response = stream_get_contents($this->redis, $size + 2);
|
719 |
+
if( ! $response)
|
720 |
+
throw new CredisException('Error reading reply.');
|
721 |
+
$response = substr($response, 0, $size);
|
722 |
+
break;
|
723 |
+
/* Multi-bulk reply */
|
724 |
+
case '*':
|
725 |
+
$count = substr($reply, 1);
|
726 |
+
if ($count == '-1') return FALSE;
|
727 |
+
|
728 |
+
$response = array();
|
729 |
+
for ($i = 0; $i < $count; $i++) {
|
730 |
+
$response[] = $this->read_reply();
|
731 |
+
}
|
732 |
+
break;
|
733 |
+
/* Integer reply */
|
734 |
+
case ':':
|
735 |
+
$response = intval(substr($reply, 1));
|
736 |
+
break;
|
737 |
+
default:
|
738 |
+
throw new CredisException('Invalid response: '.print_r($reply, TRUE));
|
739 |
+
break;
|
740 |
+
}
|
741 |
+
|
742 |
+
// Smooth over differences between phpredis and standalone response
|
743 |
+
switch($name)
|
744 |
+
{
|
745 |
+
case '': // Minor optimization for multi-bulk replies
|
746 |
+
break;
|
747 |
+
case 'config':
|
748 |
+
case 'hgetall':
|
749 |
+
$keys = $values = array();
|
750 |
+
while($response) {
|
751 |
+
$keys[] = array_shift($response);
|
752 |
+
$values[] = array_shift($response);
|
753 |
+
}
|
754 |
+
$response = count($keys) ? array_combine($keys, $values) : array();
|
755 |
+
break;
|
756 |
+
case 'info':
|
757 |
+
$lines = explode(CRLF, trim($response,CRLF));
|
758 |
+
$response = array();
|
759 |
+
foreach($lines as $line) {
|
760 |
+
if ( ! $line || substr($line, 0, 1) == '#') {
|
761 |
+
continue;
|
762 |
+
}
|
763 |
+
list($key, $value) = explode(':', $line, 2);
|
764 |
+
$response[$key] = $value;
|
765 |
+
}
|
766 |
+
break;
|
767 |
+
case 'ttl':
|
768 |
+
if($response === -1) {
|
769 |
+
$response = FALSE;
|
770 |
+
}
|
771 |
+
break;
|
772 |
+
}
|
773 |
+
|
774 |
+
return $response;
|
775 |
+
}
|
776 |
+
|
777 |
+
/**
|
778 |
+
* Build the Redis unified protocol command
|
779 |
+
*
|
780 |
+
* @param array $args
|
781 |
+
* @return string
|
782 |
+
*/
|
783 |
+
private static function _prepare_command($args)
|
784 |
+
{
|
785 |
+
return sprintf('*%d%s%s%s', count($args), CRLF, implode(array_map(array('self', '_map'), $args), CRLF), CRLF);
|
786 |
+
}
|
787 |
+
|
788 |
+
private static function _map($arg)
|
789 |
+
{
|
790 |
+
return sprintf('$%d%s%s', strlen($arg), CRLF, $arg);
|
791 |
+
}
|
792 |
+
|
793 |
+
}
|
package.xml
ADDED
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?xml version="1.0"?>
|
2 |
+
<package>
|
3 |
+
<name>Lib_Credis</name>
|
4 |
+
<version>1.8.0.0</version>
|
5 |
+
<stability>stable</stability>
|
6 |
+
<license uri="http://opensource.org/licenses/osl-3.0.php">OSL v3.0</license>
|
7 |
+
<channel>community</channel>
|
8 |
+
<extends/>
|
9 |
+
<summary>Credis Library</summary>
|
10 |
+
<description>Credis Library</description>
|
11 |
+
<notes>1.8.0.0</notes>
|
12 |
+
<authors><author><name>Magento Core Team</name><user>core</user><email>core@magentocommerce.com</email></author></authors>
|
13 |
+
<date>2013-09-24</date>
|
14 |
+
<time>09:09:42</time>
|
15 |
+
<contents><target name="magelib"><dir name="Credis"><file name="Client.php" hash="7bf903229fda050175c4f52b317c13b3"/></dir></target></contents>
|
16 |
+
<compatible/>
|
17 |
+
<dependencies><required><php><min>5.2.0</min><max>6.0.0</max></php></required></dependencies>
|
18 |
+
</package>
|