Lib_Credis - Version 1.8.0.0

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

Files changed (2) hide show
  1. lib/Credis/Client.php +793 -0
  2. 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>