replace Redisent with Credis (also adds native phpredis support)

This commit is contained in:
Chris Boulton 2013-01-12 23:37:38 +11:00
parent a6eb8e1c45
commit 2ba15eb555
11 changed files with 81 additions and 919 deletions

View File

@ -2,6 +2,14 @@
**Note:** This release introduces backwards incompatible changes with all previous versions of php-resque. Please see below for details. **Note:** This release introduces backwards incompatible changes with all previous versions of php-resque. Please see below for details.
### Redisent (Redis Library) Replaced with Credis
Redisent has always been the Redis backend for php-resque because of its lightweight nature. Unfortunately, Redisent is largely unmaintained.
[Credis](http://example.com/) is a fork of Redisent, which among other improvements will automatically use the [phpredis](https://github.com/nicolasff/phpredis) native PHP extension if it is available. (you want this for speed, trust me)
php-resque now utilizes Credis for all Redis based operations. Credis automatically required and installed as a Composer dependency.
### Composer Support ### Composer Support
Composer support has been improved and is now the recommended method for including php-resque in your project. Details on Composer support can be found in the Getting Started section of the readme. Composer support has been improved and is now the recommended method for including php-resque in your project. Details on Composer support can be found in the Getting Started section of the readme.

View File

@ -11,8 +11,15 @@
"email": "chris@bigcommerce.com" "email": "chris@bigcommerce.com"
} }
], ],
"repositories": [
{
"type": "vcs",
"url": "https://github.com/chrisboulton/credis"
}
],
"require": { "require": {
"php": ">=5.3.0" "php": ">=5.3.0",
"colinmollenhour/credis": "dev-master"
}, },
"require-dev": { "require-dev": {
"phpunit/phpunit": "3.7.*" "phpunit/phpunit": "3.7.*"

399
composer.lock generated
View File

@ -1,408 +1,53 @@
{ {
"hash": "b05c2c31be6cac834e33b1a7fe61d063", "hash": "af626b3a277bd0ab503c2c107327a88e",
"packages": [ "packages": [
],
"packages-dev": [
{ {
"name": "phpunit/php-code-coverage", "name": "colinmollenhour/credis",
"version": "1.2.7", "version": "dev-master",
"source": { "source": {
"type": "git", "type": "git",
"url": "git://github.com/sebastianbergmann/php-code-coverage.git", "url": "https://github.com/chrisboulton/credis",
"reference": "1.2.7" "reference": "62c73dd16e08069e3fd8f224cb4a5ddd73db8095"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://github.com/sebastianbergmann/php-code-coverage/archive/1.2.7.zip", "url": "https://api.github.com/repos/chrisboulton/credis/zipball/62c73dd16e08069e3fd8f224cb4a5ddd73db8095",
"reference": "1.2.7", "reference": "62c73dd16e08069e3fd8f224cb4a5ddd73db8095",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"php": ">=5.3.3", "php": ">=5.3.0"
"phpunit/php-file-iterator": ">=1.3.0@stable",
"phpunit/php-token-stream": ">=1.1.3@stable",
"phpunit/php-text-template": ">=1.1.1@stable"
}, },
"suggest": { "time": "2013-01-12 10:15:31",
"ext-dom": "*",
"ext-xdebug": ">=2.0.5"
},
"time": "2012-12-02 14:54:55",
"type": "library", "type": "library",
"autoload": { "autoload": {
"classmap": [ "classmap": [
"PHP/" "Client.php",
"Cluster.php"
] ]
}, },
"notification-url": "https://packagist.org/downloads/",
"include-path": [
""
],
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Sebastian Bergmann",
"email": "sb@sebastian-bergmann.de",
"role": "lead"
}
],
"description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.",
"homepage": "https://github.com/sebastianbergmann/php-code-coverage",
"keywords": [
"testing",
"coverage",
"xunit"
]
},
{
"name": "phpunit/php-file-iterator",
"version": "1.3.3",
"source": {
"type": "git",
"url": "git://github.com/sebastianbergmann/php-file-iterator.git",
"reference": "1.3.3"
},
"dist": {
"type": "zip",
"url": "https://github.com/sebastianbergmann/php-file-iterator/zipball/1.3.3",
"reference": "1.3.3",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"time": "2012-10-11 04:44:38",
"type": "library",
"autoload": {
"classmap": [
"File/"
]
},
"notification-url": "https://packagist.org/downloads/",
"include-path": [
""
],
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Sebastian Bergmann",
"email": "sb@sebastian-bergmann.de",
"role": "lead"
}
],
"description": "FilterIterator implementation that filters files based on a list of suffixes.",
"homepage": "http://www.phpunit.de/",
"keywords": [
"filesystem",
"iterator"
]
},
{
"name": "phpunit/php-text-template",
"version": "1.1.4",
"source": {
"type": "git",
"url": "git://github.com/sebastianbergmann/php-text-template.git",
"reference": "1.1.4"
},
"dist": {
"type": "zip",
"url": "https://github.com/sebastianbergmann/php-text-template/zipball/1.1.4",
"reference": "1.1.4",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"time": "2012-10-31 11:15:28",
"type": "library",
"autoload": {
"classmap": [
"Text/"
]
},
"notification-url": "https://packagist.org/downloads/",
"include-path": [
""
],
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Sebastian Bergmann",
"email": "sb@sebastian-bergmann.de",
"role": "lead"
}
],
"description": "Simple template engine.",
"homepage": "https://github.com/sebastianbergmann/php-text-template/",
"keywords": [
"template"
]
},
{
"name": "phpunit/php-timer",
"version": "1.0.4",
"source": {
"type": "git",
"url": "git://github.com/sebastianbergmann/php-timer.git",
"reference": "1.0.4"
},
"dist": {
"type": "zip",
"url": "https://github.com/sebastianbergmann/php-timer/zipball/1.0.4",
"reference": "1.0.4",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"time": "2012-10-11 04:45:58",
"type": "library",
"autoload": {
"classmap": [
"PHP/"
]
},
"notification-url": "https://packagist.org/downloads/",
"include-path": [
""
],
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Sebastian Bergmann",
"email": "sb@sebastian-bergmann.de",
"role": "lead"
}
],
"description": "Utility class for timing",
"homepage": "http://www.phpunit.de/",
"keywords": [
"timer"
]
},
{
"name": "phpunit/php-token-stream",
"version": "1.1.5",
"source": {
"type": "git",
"url": "git://github.com/sebastianbergmann/php-token-stream.git",
"reference": "1.1.5"
},
"dist": {
"type": "zip",
"url": "https://github.com/sebastianbergmann/php-token-stream/zipball/1.1.5",
"reference": "1.1.5",
"shasum": ""
},
"require": {
"ext-tokenizer": "*",
"php": ">=5.3.3"
},
"time": "2012-10-11 04:47:14",
"type": "library",
"autoload": {
"classmap": [
"PHP/"
]
},
"notification-url": "https://packagist.org/downloads/",
"include-path": [
""
],
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Sebastian Bergmann",
"email": "sb@sebastian-bergmann.de",
"role": "lead"
}
],
"description": "Wrapper around PHP's tokenizer extension.",
"homepage": "http://www.phpunit.de/",
"keywords": [
"tokenizer"
]
},
{
"name": "phpunit/phpunit",
"version": "3.7.12",
"source": {
"type": "git",
"url": "git://github.com/sebastianbergmann/phpunit.git",
"reference": "3.7.12"
},
"dist": {
"type": "zip",
"url": "https://github.com/sebastianbergmann/phpunit/archive/3.7.12.zip",
"reference": "3.7.12",
"shasum": ""
},
"require": {
"php": ">=5.3.3",
"phpunit/php-file-iterator": ">=1.3.1",
"phpunit/php-text-template": ">=1.1.1",
"phpunit/php-code-coverage": ">=1.2.1",
"phpunit/php-timer": ">=1.0.2",
"phpunit/phpunit-mock-objects": ">=1.2.0,<1.3.0",
"symfony/yaml": ">=2.1.0,<2.2.0",
"ext-dom": "*",
"ext-pcre": "*",
"ext-reflection": "*",
"ext-spl": "*"
},
"suggest": {
"phpunit/php-invoker": ">=1.1.0",
"ext-json": "*",
"ext-simplexml": "*",
"ext-tokenizer": "*"
},
"time": "2013-01-09 22:41:02",
"bin": [
"composer/bin/phpunit"
],
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "3.7.x-dev"
}
},
"autoload": {
"classmap": [
"PHPUnit/"
]
},
"notification-url": "https://packagist.org/downloads/",
"include-path": [
"",
"../../symfony/yaml/"
],
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Sebastian Bergmann",
"email": "sebastian@phpunit.de",
"role": "lead"
}
],
"description": "The PHP Unit Testing framework.",
"homepage": "http://www.phpunit.de/",
"keywords": [
"testing",
"phpunit",
"xunit"
]
},
{
"name": "phpunit/phpunit-mock-objects",
"version": "1.2.2",
"source": {
"type": "git",
"url": "git://github.com/sebastianbergmann/phpunit-mock-objects.git",
"reference": "1.2.2"
},
"dist": {
"type": "zip",
"url": "https://github.com/sebastianbergmann/phpunit-mock-objects/archive/1.2.2.zip",
"reference": "1.2.2",
"shasum": ""
},
"require": {
"php": ">=5.3.3",
"phpunit/php-text-template": ">=1.1.1@stable"
},
"suggest": {
"ext-soap": "*"
},
"time": "2012-11-05 10:39:13",
"type": "library",
"autoload": {
"classmap": [
"PHPUnit/"
]
},
"notification-url": "https://packagist.org/downloads/",
"include-path": [
""
],
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Sebastian Bergmann",
"email": "sb@sebastian-bergmann.de",
"role": "lead"
}
],
"description": "Mock Object library for PHPUnit",
"homepage": "https://github.com/sebastianbergmann/phpunit-mock-objects/",
"keywords": [
"mock",
"xunit"
]
},
{
"name": "symfony/yaml",
"version": "v2.1.6",
"target-dir": "Symfony/Component/Yaml",
"source": {
"type": "git",
"url": "https://github.com/symfony/Yaml",
"reference": "v2.1.6"
},
"dist": {
"type": "zip",
"url": "https://github.com/symfony/Yaml/archive/v2.1.6.zip",
"reference": "v2.1.6",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"time": "2012-12-06 10:00:55",
"type": "library",
"autoload": {
"psr-0": {
"Symfony\\Component\\Yaml": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [ "license": [
"MIT" "MIT"
], ],
"authors": [ "authors": [
{ {
"name": "Fabien Potencier", "name": "Colin Mollenhour",
"email": "fabien@symfony.com" "email": "colin@mollenhour.com"
},
{
"name": "Symfony Community",
"homepage": "http://symfony.com/contributors"
} }
], ],
"description": "Symfony Yaml Component", "description": "Credis is a lightweight interface to the Redis key-value store which wraps the phpredis library when available for better performance.",
"homepage": "http://symfony.com" "homepage": "https://github.com/colinmollenhour/credis",
"support": {
"source": "https://github.com/chrisboulton/credis/tree/master"
}
} }
], ],
"packages-dev": null,
"aliases": [ "aliases": [
], ],
"minimum-stability": "stable", "minimum-stability": "stable",
"stability-flags": [ "stability-flags": {
"colinmollenhour/credis": 20
] }
} }

View File

@ -1,22 +0,0 @@
Copyright (c) 2009 Justin Poliey <jdp34@njit.edu>
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.

View File

@ -1,67 +0,0 @@
# Redisent
Redisent is a simple, no-nonsense interface to the [Redis](http://code.google.com/p/redis/) key-value store for modest developers.
Due to the way it is implemented, it is flexible and tolerant of changes to the Redis protocol.
## Getting to work
If you're at all familiar with the Redis protocol and PHP objects, you've already mastered Redisent.
All Redisent does is map the Redis protocol to a PHP object, abstract away the nitty-gritty, and make the return values PHP compatible.
require 'redisent.php';
$redis = new Redisent('localhost');
$redis->set('awesome', 'absolutely');
echo sprintf('Is Redisent awesome? %s.\n', $redis->get('awesome'));
You use the exact same command names, and the exact same argument order. **How wonderful.** How about a more complex example?
require 'redisent.php';
$redis = new Redisent('localhost');
$redis->rpush('particles', 'proton');
$redis->rpush('particles', 'electron');
$redis->rpush('particles', 'neutron');
$particles = $redis->lrange('particles', 0, -1);
$particle_count = $redis->llen('particles');
echo "<p>The {$particle_count} particles that make up atoms are:</p>";
echo "<ul>";
foreach ($particles as $particle) {
echo "<li>{$particle}</li>";
}
echo "</ul>";
Be aware that Redis error responses will be wrapped in a RedisException class and thrown, so do be sure to use proper coding techniques.
## Clustering your servers
Redisent also includes a way for developers to fully utilize the scalability of Redis with multiple servers and [consistent hashing](http://en.wikipedia.org/wiki/Consistent_hashing).
Using the RedisentCluster class, you can use Redisent the same way, except that keys will be hashed across multiple servers.
Here is how to set up a cluster:
include 'redisent_cluster.php';
$cluster = new RedisentCluster(array(
array('host' => '127.0.0.1', 'port' => 6379),
array('host' => '127.0.0.1', 'port' => 6380)
));
You can then use Redisent the way you normally would, i.e., `$cluster->set('key', 'value')` or `$cluster->lrange('particles', 0, -1)`.
But what about when you need to use commands that are server specific and do not operate on keys? You can use routing, with the `RedisentCluster::to` method.
To use routing, you need to assign a server an alias in the constructor of the Redis cluster. Aliases are not required on all servers, just the ones you want to be able to access directly.
include 'redisent_cluster.php';
$cluster = new RedisentCluster(array(
'alpha' => array('host' => '127.0.0.1', 'port' => 6379),
array('host' => '127.0.0.1', 'port' => 6380)
));
Now there is an alias of the server running on 127.0.0.1:6379 called **alpha**, and can be interacted with like this:
// get server info
$cluster->to('alpha')->info();
Now you have complete programatic control over your Redis servers.
## About
&copy; 2009 [Justin Poliey](http://justinpoliey.com)

View File

@ -1,165 +0,0 @@
<?php
/**
* Redisent, a Redis interface for the modest
* @author Justin Poliey <jdp34@njit.edu>
* @copyright 2009 Justin Poliey <jdp34@njit.edu>
* @license http://www.opensource.org/licenses/mit-license.php The MIT License
* @package Redisent
*/
define('CRLF', sprintf('%s%s', chr(13), chr(10)));
/**
* Wraps native Redis errors in friendlier PHP exceptions
* Only declared if class doesn't already exist to ensure compatibility with php-redis
*/
if (! class_exists('RedisException', false)) {
class RedisException extends Exception {
}
}
/**
* Redisent, a Redis interface for the modest among us
*/
class Redisent {
/**
* Socket connection to the Redis server
* @var resource
* @access private
*/
private $__sock;
/**
* Host of the Redis server
* @var string
* @access public
*/
public $host;
/**
* Port on which the Redis server is running
* @var integer
* @access public
*/
public $port;
/**
* Number of times to attempt a reconnect
*
* @var int
*/
public $max_reconnects = 3;
/**
* Creates a Redisent connection to the Redis server on host {@link $host} and port {@link $port}.
* @param string $host The hostname of the Redis server
* @param integer $port The port number of the Redis server
*/
function __construct($host, $port = 6379) {
$this->host = $host;
$this->port = $port;
$this->establishConnection();
}
function establishConnection() {
$this->__sock = fsockopen($this->host, $this->port, $errno, $errstr);
if (!$this->__sock) {
throw new Exception("{$errno} - {$errstr}");
}
}
function __destruct() {
fclose($this->__sock);
}
function __call($name, $args) {
/* Build the Redis unified protocol command */
array_unshift($args, strtoupper($name));
$command = sprintf('*%d%s%s%s', count($args), CRLF, implode(array_map(array($this, 'formatArgument'), $args), CRLF), CRLF);
/* Open a Redis connection and execute the command */
$reconnects = 0;
for ($written = 0; $written < strlen($command); $written += $fwrite) {
$fwrite = @fwrite($this->__sock, substr($command, $written));
if ($fwrite === FALSE || $fwrite === 0) {
if ($reconnects >= (int)$this->max_reconnects) {
throw new Exception('Failed to write entire command to stream');
}else{
fclose($this->__sock);
sleep(1);
$this->establishConnection();
$reconnects++;
}
}
}
/* Parse the response based on the reply identifier */
$reply = trim(fgets($this->__sock, 512));
switch (substr($reply, 0, 1)) {
/* Error reply */
case '-':
throw new RedisException(substr(trim($reply), 4));
break;
/* Inline reply */
case '+':
$response = substr(trim($reply), 1);
break;
/* Bulk reply */
case '$':
$response = null;
if ($reply == '$-1') {
break;
}
$read = 0;
$size = substr($reply, 1);
do {
$block_size = ($size - $read) > 1024 ? 1024 : ($size - $read);
$response .= fread($this->__sock, $block_size);
$read += $block_size;
} while ($read < $size);
fread($this->__sock, 2); /* discard crlf */
break;
/* Multi-bulk reply */
case '*':
$count = substr($reply, 1);
if ($count == '-1') {
return null;
}
$response = array();
for ($i = 0; $i < $count; $i++) {
$bulk_head = trim(fgets($this->__sock, 512));
$size = substr($bulk_head, 1);
if ($size == '-1') {
$response[] = null;
}
else {
$read = 0;
$block = "";
do {
$block_size = ($size - $read) > 1024 ? 1024 : ($size - $read);
$block .= fread($this->__sock, $block_size);
$read += $block_size;
} while ($read < $size);
fread($this->__sock, 2); /* discard crlf */
$response[] = $block;
}
}
break;
/* Integer reply */
case ':':
$response = intval(substr(trim($reply), 1));
break;
default:
throw new RedisException("invalid server response: {$reply}");
break;
}
/* Party on */
return $response;
}
private function formatArgument($arg) {
return sprintf('$%d%s%s', strlen($arg), CRLF, $arg);
}
}

View File

@ -1,138 +0,0 @@
<?php
/**
* Redisent, a Redis interface for the modest
* @author Justin Poliey <jdp34@njit.edu>
* @copyright 2009 Justin Poliey <jdp34@njit.edu>
* @license http://www.opensource.org/licenses/mit-license.php The MIT License
* @package Redisent
*/
require_once __DIR__ . '/Redisent.php';
/**
* A generalized Redisent interface for a cluster of Redis servers
*/
class RedisentCluster {
/**
* Collection of Redisent objects attached to Redis servers
* @var array
* @access private
*/
private $redisents;
/**
* Aliases of Redisent objects attached to Redis servers, used to route commands to specific servers
* @see RedisentCluster::to
* @var array
* @access private
*/
private $aliases;
/**
* Hash ring of Redis server nodes
* @var array
* @access private
*/
private $ring;
/**
* Individual nodes of pointers to Redis servers on the hash ring
* @var array
* @access private
*/
private $nodes;
/**
* Number of replicas of each node to make around the hash ring
* @var integer
* @access private
*/
private $replicas = 128;
/**
* The commands that are not subject to hashing
* @var array
* @access private
*/
private $dont_hash = array(
'RANDOMKEY', 'DBSIZE',
'SELECT', 'MOVE', 'FLUSHDB', 'FLUSHALL',
'SAVE', 'BGSAVE', 'LASTSAVE', 'SHUTDOWN',
'INFO', 'MONITOR', 'SLAVEOF'
);
/**
* Creates a Redisent interface to a cluster of Redis servers
* @param array $servers The Redis servers in the cluster. Each server should be in the format array('host' => hostname, 'port' => port)
*/
function __construct($servers) {
$this->ring = array();
$this->aliases = array();
foreach ($servers as $alias => $server) {
$this->redisents[] = new Redisent($server['host'], $server['port']);
if (is_string($alias)) {
$this->aliases[$alias] = $this->redisents[count($this->redisents)-1];
}
for ($replica = 1; $replica <= $this->replicas; $replica++) {
$this->ring[crc32($server['host'].':'.$server['port'].'-'.$replica)] = $this->redisents[count($this->redisents)-1];
}
}
ksort($this->ring, SORT_NUMERIC);
$this->nodes = array_keys($this->ring);
}
/**
* Routes a command to a specific Redis server aliased by {$alias}.
* @param string $alias The alias of the Redis server
* @return Redisent The Redisent object attached to the Redis server
*/
function to($alias) {
if (isset($this->aliases[$alias])) {
return $this->aliases[$alias];
}
else {
throw new Exception("That Redisent alias does not exist");
}
}
/* Execute a Redis command on the cluster */
function __call($name, $args) {
/* Pick a server node to send the command to */
$name = strtoupper($name);
if (!in_array($name, $this->dont_hash)) {
$node = $this->nextNode(crc32($args[0]));
$redisent = $this->ring[$node];
}
else {
$redisent = $this->redisents[0];
}
/* Execute the command on the server */
return call_user_func_array(array($redisent, $name), $args);
}
/**
* Routes to the proper server node
* @param integer $needle The hash value of the Redis command
* @return Redisent The Redisent object associated with the hash
*/
private function nextNode($needle) {
$haystack = $this->nodes;
while (count($haystack) > 2) {
$try = floor(count($haystack) / 2);
if ($haystack[$try] == $needle) {
return $needle;
}
if ($needle < $haystack[$try]) {
$haystack = array_slice($haystack, 0, $try + 1);
}
if ($needle > $haystack[$try]) {
$haystack = array_slice($haystack, $try + 1);
}
}
return $haystack[count($haystack)-1];
}
}

View File

@ -71,21 +71,7 @@ class Resque
$server = 'localhost:6379'; $server = 'localhost:6379';
} }
if(is_array($server)) { self::$redis = new Resque_Redis($server, self::$redisDatabase);
self::$redis = new Resque_RedisCluster($server);
}
else {
if (strpos($server, 'unix:') === false) {
list($host, $port) = explode(':', $server);
}
else {
$host = $server;
$port = null;
}
self::$redis = new Resque_Redis($host, $port);
}
self::$redis->select(self::$redisDatabase);
return self::$redis; return self::$redis;
} }

View File

@ -1,25 +1,22 @@
<?php <?php
// Third- party apps may have already loaded Resident from elsewhere
// so lets be careful.
if(!class_exists('Redisent', false)) {
require_once __DIR__ . '/../Redisent/Redisent.php';
}
/** /**
* Extended Redisent class used by Resque for all communication with * Wrap Credis to add namespace support and various helper methods.
* redis. Essentially adds namespace support to Redisent.
* *
* @package Resque/Redis * @package Resque/Redis
* @author Chris Boulton <chris@bigcommerce.com> * @author Chris Boulton <chris@bigcommerce.com>
* @license http://www.opensource.org/licenses/mit-license.php * @license http://www.opensource.org/licenses/mit-license.php
*/ */
class Resque_Redis extends Redisent class Resque_Redis
{ {
/** /**
* Redis namespace * Redis namespace
* @var string * @var string
*/ */
private static $defaultNamespace = 'resque:'; private static $defaultNamespace = 'resque:';
private $server;
private $database;
/** /**
* @var array List of all commands in Redis that supply a key as their * @var array List of all commands in Redis that supply a key as their
* first argument. Used to prefix keys with the Resque namespace. * first argument. Used to prefix keys with the Resque namespace.
@ -81,7 +78,7 @@ class Resque_Redis extends Redisent
// msetnx // msetnx
// mset // mset
// renamenx // renamenx
/** /**
* Set Redis namespace (prefix) default: resque * Set Redis namespace (prefix) default: resque
* @param string $namespace * @param string $namespace
@ -93,7 +90,36 @@ class Resque_Redis extends Redisent
} }
self::$defaultNamespace = $namespace; self::$defaultNamespace = $namespace;
} }
public function __construct($server, $database = null)
{
$this->server = $server;
$this->database = $database;
if (is_array($this->server)) {
$this->driver = new Credis_Cluster($server);
}
else {
$port = null;
$host = $server;
// If not a UNIX socket path or tcp:// formatted connections string
// assume host:port combination.
if (strpos($server, '/') === false) {
$parts = explode(':', $server);
if (isset($parts[1])) {
$port = $parts[1];
}
$host = $parts[0];
}
$this->driver = new Credis_Client($host, $port);
}
if ($this->database !== null) {
$this->driver->select($database);
}
}
/** /**
* Magic method to handle all function requests and prefix key based * Magic method to handle all function requests and prefix key based
* operations with the {self::$defaultNamespace} key prefix. * operations with the {self::$defaultNamespace} key prefix.
@ -103,14 +129,13 @@ class Resque_Redis extends Redisent
* @return mixed Return value from Resident::call() based on the command. * @return mixed Return value from Resident::call() based on the command.
*/ */
public function __call($name, $args) { public function __call($name, $args) {
$args = func_get_args();
if(in_array($name, $this->keyCommands)) { if(in_array($name, $this->keyCommands)) {
$args[1][0] = self::$defaultNamespace . $args[1][0]; $args[0] = self::$defaultNamespace . $args[0];
} }
try { try {
return parent::__call($name, $args[1]); return $this->driver->__call($name, $args);
} }
catch(RedisException $e) { catch(CredisException $e) {
return false; return false;
} }
} }
@ -130,4 +155,4 @@ class Resque_Redis extends Redisent
return $string; return $string;
} }
} }
?> ?>

View File

@ -1,117 +0,0 @@
<?php
// Third- party apps may have already loaded Resident from elsewhere
// so lets be careful.
if(!class_exists('RedisentCluster', false)) {
require_once __DIR__ . '/../Redisent/RedisentCluster.php';
}
/**
* Extended Redisent class used by Resque for all communication with
* redis. Essentially adds namespace support to Redisent.
*
* @package Resque/Redis
* @author Chris Boulton <chris@bigcommerce.com>
* @license http://www.opensource.org/licenses/mit-license.php
*/
class Resque_RedisCluster extends RedisentCluster
{
/**
* Redis namespace
* @var string
*/
private static $defaultNamespace = 'resque:';
/**
* @var array List of all commands in Redis that supply a key as their
* first argument. Used to prefix keys with the Resque namespace.
*/
private $keyCommands = array(
'exists',
'del',
'type',
'keys',
'expire',
'ttl',
'move',
'set',
'get',
'getset',
'setnx',
'incr',
'incrby',
'decrby',
'decrby',
'rpush',
'lpush',
'llen',
'lrange',
'ltrim',
'lindex',
'lset',
'lrem',
'lpop',
'rpop',
'sadd',
'srem',
'spop',
'scard',
'sismember',
'smembers',
'srandmember',
'zadd',
'zrem',
'zrange',
'zrevrange',
'zrangebyscore',
'zcard',
'zscore',
'zremrangebyscore',
'sort'
);
// sinterstore
// sunion
// sunionstore
// sdiff
// sdiffstore
// sinter
// smove
// rename
// rpoplpush
// mget
// msetnx
// mset
// renamenx
/**
* Set Redis namespace (prefix) default: resque
* @param string $namespace
*/
public static function prefix($namespace)
{
if (strpos($namespace, ':') === false) {
$namespace .= ':';
}
self::$defaultNamespace = $namespace;
}
/**
* Magic method to handle all function requests and prefix key based
* operations with the '{self::$defaultNamespace}' key prefix.
*
* @param string $name The name of the method called.
* @param array $args Array of supplied arguments to the method.
* @return mixed Return value from Resident::call() based on the command.
*/
public function __call($name, $args) {
$args = func_get_args();
if(in_array($name, $this->keyCommands)) {
$args[1][0] = self::$defaultNamespace . $args[1][0];
}
try {
return parent::__call($name, $args[1]);
}
catch(RedisException $e) {
return false;
}
}
}
?>

View File

@ -15,7 +15,7 @@ class Resque_Tests_TestCase extends PHPUnit_Framework_TestCase
{ {
$config = file_get_contents(REDIS_CONF); $config = file_get_contents(REDIS_CONF);
preg_match('#^\s*port\s+([0-9]+)#m', $config, $matches); preg_match('#^\s*port\s+([0-9]+)#m', $config, $matches);
$this->redis = new Redisent('localhost', $matches[1]); $this->redis = new Credis_Client('localhost', $matches[1]);
// Flush redis // Flush redis
$this->redis->flushAll(); $this->redis->flushAll();