mirror of
https://github.com/predis/predis.git
synced 2026-08-19 18:42:35 +00:00
Compare commits
33 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| bd961a767b | |||
| b3599a8e6b | |||
| 0b46de424e | |||
| 73bdfc2d72 | |||
| 3077e76708 | |||
| 208fdf6daf | |||
| 7d7995ff8f | |||
| 418197af75 | |||
| 90cb9d437d | |||
| 4668bdca83 | |||
| 93a3c184ed | |||
| f073bc8f70 | |||
| 50970c003b | |||
| 34cfebdc14 | |||
| 735c92089c | |||
| 3853e62d34 | |||
| ed105ab4fc | |||
| adb489b4d3 | |||
| 6d1c4a2096 | |||
| 5cf10ddbfa | |||
| 3b74aa8f82 | |||
| ac2b718387 | |||
| 89e46912a2 | |||
| 200d864df3 | |||
| 35b8588dc4 | |||
| 371a7ad69b | |||
| 5ae476ee16 | |||
| f4f19ace0a | |||
| 50092d1421 | |||
| 5287bb2387 | |||
| dd07c12402 | |||
| 42aa8333fc | |||
| 25ac5c3ca4 |
@@ -1,3 +1,42 @@
|
||||
v0.6.1 (2010-07-11)
|
||||
* Minor internal improvements and clean ups.
|
||||
|
||||
* New commands available in the Redis v2.2 profile (dev):
|
||||
- Misc. : WATCH, UNWATCH
|
||||
|
||||
* Optional modifiers for ZRANGE, ZREVRANGE and ZRANGEBYSCORE queries are
|
||||
supported using an associative array passed as the last argument of their
|
||||
respective methods.
|
||||
|
||||
* The LIMIT modifier for ZRANGEBYSCORE can be specified using either:
|
||||
- an indexed array: array($offset, $count)
|
||||
- an associative array: array('offset' => $offset, 'count' => $count)
|
||||
|
||||
* The method Predis\Client::__construct() now accepts also instances of
|
||||
Predis\ConnectionParameters.
|
||||
|
||||
* Predis\MultiExecBlock and Predis\PubSubContext now throw an exception
|
||||
when trying to create their instances using a profile that does not
|
||||
support the required Redis commands or when the client is connected to
|
||||
a cluster of connections.
|
||||
|
||||
* Various improvements to Predis\MultiExecBlock:
|
||||
- fixes and more consistent behaviour across various usage cases.
|
||||
- support for WATCH and UNWATCH when using the current development
|
||||
profile (Redis v2.2) and aborted transactions.
|
||||
|
||||
* New signature for Predis\Client::multiExec() which is now able to accept
|
||||
an array of options for the underlying instance of Predis\MultiExecBlock.
|
||||
Backwards compatibility with previous releases of Predis is ensured.
|
||||
|
||||
* New signature for Predis\Client::pipeline() which is now able to accept
|
||||
an array of options for the underlying instance of Predis\CommandPipeline.
|
||||
Backwards compatibility with previous releases of Predis is ensured.
|
||||
The method Predis\Client::pipelineSafe() is to be considered deprecated.
|
||||
|
||||
* FIX: The WEIGHT modifier for ZUNIONSTORE and ZINTERSTORE was handled
|
||||
incorrectly with more than two weights specified.
|
||||
|
||||
v0.6.0 (2010-05-24)
|
||||
* Switched to the new multi-bulk request protocol for all of the commands
|
||||
in the Redis 1.2 and Redis 2.0 profiles. Inline and bulk requests are now
|
||||
|
||||
+268
-54
@@ -3,6 +3,7 @@ namespace Predis;
|
||||
|
||||
class PredisException extends \Exception { }
|
||||
class ClientException extends PredisException { } // Client-side errors
|
||||
class AbortedMultiExec extends PredisException { } // Aborted multi/exec
|
||||
|
||||
class ServerException extends PredisException { // Server-side errors
|
||||
public function toResponseError() {
|
||||
@@ -110,7 +111,9 @@ class Client {
|
||||
}
|
||||
|
||||
private function createConnection($parameters) {
|
||||
$params = new ConnectionParameters($parameters);
|
||||
$params = $parameters instanceof ConnectionParameters
|
||||
? $parameters
|
||||
: new ConnectionParameters($parameters);
|
||||
$connection = new Connection($params, $this->_responseReader);
|
||||
|
||||
if ($params->password !== null) {
|
||||
@@ -152,7 +155,7 @@ class Client {
|
||||
}
|
||||
|
||||
public function getClientFor($connectionAlias) {
|
||||
if (!($this->_connection instanceof ConnectionCluster)) {
|
||||
if (!Shared\Utils::isCluster($this->_connection)) {
|
||||
throw new ClientException(
|
||||
'This method is supported only when the client is connected to a cluster of connections'
|
||||
);
|
||||
@@ -188,7 +191,7 @@ class Client {
|
||||
return $this->_connection;
|
||||
}
|
||||
else {
|
||||
return $this->_connection instanceof ConnectionCluster
|
||||
return Shared\Utils::isCluster($this->_connection)
|
||||
? $this->_connection->getConnectionById($id)
|
||||
: $this->_connection;
|
||||
}
|
||||
@@ -209,7 +212,7 @@ class Client {
|
||||
|
||||
public function executeCommandOnShards(Command $command) {
|
||||
$replies = array();
|
||||
if ($this->_connection instanceof \Predis\ConnectionCluster) {
|
||||
if (Shared\Utils::isCluster($this->_connection)) {
|
||||
foreach($this->_connection as $connection) {
|
||||
$replies[] = $connection->executeCommand($command);
|
||||
}
|
||||
@@ -221,22 +224,50 @@ class Client {
|
||||
}
|
||||
|
||||
public function rawCommand($rawCommandData, $closesConnection = false) {
|
||||
if ($this->_connection instanceof \Predis\ConnectionCluster) {
|
||||
if (Shared\Utils::isCluster($this->_connection)) {
|
||||
throw new ClientException('Cannot send raw commands when connected to a cluster of Redis servers');
|
||||
}
|
||||
return $this->_connection->rawCommand($rawCommandData, $closesConnection);
|
||||
}
|
||||
|
||||
public function pipeline($pipelineBlock = null) {
|
||||
return $this->pipelineExecute(new CommandPipeline($this), $pipelineBlock);
|
||||
public function pipeline(/* arguments */) {
|
||||
$argv = func_get_args();
|
||||
$argc = func_num_args();
|
||||
|
||||
if ($argc === 0) {
|
||||
return $this->initPipeline();
|
||||
}
|
||||
else if ($argc === 1) {
|
||||
list($arg0) = $argv;
|
||||
return is_array($arg0) ? $this->initPipeline($arg0) : $this->initPipeline(null, $arg0);
|
||||
}
|
||||
else if ($argc === 2) {
|
||||
list($arg0, $arg1) = $argv;
|
||||
return $this->initPipeline($arg0, $arg1);
|
||||
}
|
||||
}
|
||||
|
||||
public function pipelineSafe($pipelineBlock = null) {
|
||||
$connection = $this->getConnection();
|
||||
$pipeline = new CommandPipeline($this, $connection instanceof Connection
|
||||
? new Pipeline\SafeExecutor($connection)
|
||||
: new Pipeline\SafeClusterExecutor($connection)
|
||||
);
|
||||
return $this->initPipeline(array('safe' => true), $pipelineBlock);
|
||||
}
|
||||
|
||||
private function initPipeline(Array $options = null, $pipelineBlock = null) {
|
||||
$pipeline = null;
|
||||
if (isset($options)) {
|
||||
if (isset($options['safe']) && $options['safe'] == true) {
|
||||
$connection = $this->getConnection();
|
||||
$pipeline = new CommandPipeline($this, $connection instanceof Connection
|
||||
? new Pipeline\SafeExecutor($connection)
|
||||
: new Pipeline\SafeClusterExecutor($connection)
|
||||
);
|
||||
}
|
||||
else {
|
||||
$pipeline = new CommandPipeline($this);
|
||||
}
|
||||
}
|
||||
else {
|
||||
$pipeline = new CommandPipeline($this);
|
||||
}
|
||||
return $this->pipelineExecute($pipeline, $pipelineBlock);
|
||||
}
|
||||
|
||||
@@ -244,9 +275,26 @@ class Client {
|
||||
return $block !== null ? $pipeline->execute($block) : $pipeline;
|
||||
}
|
||||
|
||||
public function multiExec($multiExecBlock = null) {
|
||||
$multiExec = new MultiExecBlock($this);
|
||||
return $multiExecBlock !== null ? $multiExec->execute($multiExecBlock) : $multiExec;
|
||||
public function multiExec(/* arguments */) {
|
||||
$argv = func_get_args();
|
||||
$argc = func_num_args();
|
||||
|
||||
if ($argc === 0) {
|
||||
return $this->initMultiExec();
|
||||
}
|
||||
else if ($argc === 1) {
|
||||
list($arg0) = $argv;
|
||||
return is_array($arg0) ? $this->initMultiExec($arg0) : $this->initMultiExec(null, $arg0);
|
||||
}
|
||||
else if ($argc === 2) {
|
||||
list($arg0, $arg1) = $argv;
|
||||
return $this->initMultiExec($arg0, $arg1);
|
||||
}
|
||||
}
|
||||
|
||||
private function initMultiExec(Array $options = null, $transBlock = null) {
|
||||
$multi = isset($options) ? new MultiExecBlock($this, $options) : new MultiExecBlock($this);
|
||||
return $transBlock !== null ? $multi->execute($transBlock) : $multi;
|
||||
}
|
||||
|
||||
public function pubSubContext() {
|
||||
@@ -430,8 +478,7 @@ abstract class Command {
|
||||
$this->_hash = null;
|
||||
}
|
||||
|
||||
protected function getArguments() {
|
||||
// TODO: why getArguments is protected?
|
||||
public function getArguments() {
|
||||
return isset($this->_arguments) ? $this->_arguments : array();
|
||||
}
|
||||
|
||||
@@ -574,8 +621,9 @@ class ResponseMultiBulkHandler implements IResponseHandler {
|
||||
$list = array();
|
||||
|
||||
if ($listLength > 0) {
|
||||
$reader = $connection->getResponseReader();
|
||||
for ($i = 0; $i < $listLength; $i++) {
|
||||
$list[] = $connection->getResponseReader()->read($connection);
|
||||
$list[] = $reader->read($connection);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -767,23 +815,58 @@ class CommandPipeline {
|
||||
}
|
||||
|
||||
class MultiExecBlock {
|
||||
private $_redisClient, $_commands, $_initialized, $_discarded;
|
||||
private $_initialized, $_discarded, $_insideBlock;
|
||||
private $_redisClient, $_options, $_commands;
|
||||
private $_supportsWatch;
|
||||
|
||||
public function __construct(Client $redisClient) {
|
||||
public function __construct(Client $redisClient, Array $options = null) {
|
||||
$this->checkCapabilities($redisClient);
|
||||
$this->_initialized = false;
|
||||
$this->_discarded = false;
|
||||
$this->_insideBlock = false;
|
||||
$this->_redisClient = $redisClient;
|
||||
$this->_options = $options ?: array();
|
||||
$this->_commands = array();
|
||||
}
|
||||
|
||||
private function checkCapabilities(Client $redisClient) {
|
||||
if (Shared\Utils::isCluster($redisClient->getConnection())) {
|
||||
throw new \Predis\ClientException(
|
||||
'Cannot initialize a MULTI/EXEC context over a cluster of connections'
|
||||
);
|
||||
}
|
||||
$profile = $redisClient->getProfile();
|
||||
if ($profile->supportsCommands(array('multi', 'exec', 'discard')) === false) {
|
||||
throw new \Predis\ClientException(
|
||||
'The current profile does not support MULTI, EXEC and DISCARD commands'
|
||||
);
|
||||
}
|
||||
$this->_supportsWatch = $profile->supportsCommands(array('watch', 'unwatch'));
|
||||
}
|
||||
|
||||
private function isWatchSupported() {
|
||||
if ($this->_supportsWatch === false) {
|
||||
throw new \Predis\ClientException(
|
||||
'The current profile does not support WATCH and UNWATCH commands'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private function initialize() {
|
||||
if ($this->_initialized === false) {
|
||||
if (isset($this->_options['watch'])) {
|
||||
$this->watch($this->_options['watch']);
|
||||
}
|
||||
$this->_redisClient->multi();
|
||||
$this->_initialized = true;
|
||||
$this->_discarded = false;
|
||||
}
|
||||
}
|
||||
|
||||
private function setInsideBlock($value) {
|
||||
$this->_insideBlock = $value;
|
||||
}
|
||||
|
||||
public function __call($method, $arguments) {
|
||||
$this->initialize();
|
||||
$command = $this->_redisClient->createCommand($method, $arguments);
|
||||
@@ -797,6 +880,34 @@ class MultiExecBlock {
|
||||
}
|
||||
}
|
||||
|
||||
public function watch($keys) {
|
||||
$this->isWatchSupported();
|
||||
if ($this->_initialized === true) {
|
||||
throw new \Predis\ClientException('WATCH inside MULTI is not allowed');
|
||||
}
|
||||
|
||||
$reply = null;
|
||||
if (is_array($keys)) {
|
||||
$reply = array();
|
||||
foreach ($keys as $key) {
|
||||
$reply = $this->_redisClient->watch($keys);
|
||||
}
|
||||
}
|
||||
else {
|
||||
$reply = $this->_redisClient->watch($keys);
|
||||
}
|
||||
return $reply;
|
||||
}
|
||||
|
||||
public function multi() {
|
||||
$this->initialize();
|
||||
}
|
||||
|
||||
public function unwatch() {
|
||||
$this->isWatchSupported();
|
||||
$this->_redisClient->unwatch();
|
||||
}
|
||||
|
||||
public function discard() {
|
||||
$this->_redisClient->discard();
|
||||
$this->_commands = array();
|
||||
@@ -804,7 +915,17 @@ class MultiExecBlock {
|
||||
$this->_discarded = true;
|
||||
}
|
||||
|
||||
public function exec() {
|
||||
return $this->execute();
|
||||
}
|
||||
|
||||
public function execute($block = null) {
|
||||
if ($this->_insideBlock === true) {
|
||||
throw new \Predis\ClientException(
|
||||
"Cannot invoke 'execute' or 'exec' inside an active client transaction block"
|
||||
);
|
||||
}
|
||||
|
||||
if ($block && !is_callable($block)) {
|
||||
throw new \InvalidArgumentException('Argument passed must be a callable object');
|
||||
}
|
||||
@@ -814,17 +935,21 @@ class MultiExecBlock {
|
||||
|
||||
try {
|
||||
if ($block !== null) {
|
||||
$this->setInsideBlock(true);
|
||||
$block($this);
|
||||
$this->setInsideBlock(false);
|
||||
}
|
||||
|
||||
if ($this->_discarded === true) {
|
||||
return;
|
||||
}
|
||||
|
||||
$execReply = (($reply = $this->_redisClient->exec()) instanceof \Iterator
|
||||
? iterator_to_array($reply)
|
||||
: $reply
|
||||
);
|
||||
$reply = $this->_redisClient->exec();
|
||||
if ($reply === null) {
|
||||
throw new AbortedMultiExec('The current transaction has been aborted by the server');
|
||||
}
|
||||
|
||||
$execReply = $reply instanceof \Iterator ? iterator_to_array($reply) : $reply;
|
||||
$commands = &$this->_commands;
|
||||
$sizeofReplies = count($execReply);
|
||||
|
||||
@@ -841,6 +966,7 @@ class MultiExecBlock {
|
||||
}
|
||||
}
|
||||
catch (\Exception $exception) {
|
||||
$this->setInsideBlock(false);
|
||||
$blockException = $exception;
|
||||
}
|
||||
|
||||
@@ -869,12 +995,16 @@ class PubSubContext implements \Iterator {
|
||||
const MESSAGE = 'message';
|
||||
const PMESSAGE = 'pmessage';
|
||||
|
||||
const STATUS_VALID = 0x0001;
|
||||
const STATUS_SUBSCRIBED = 0x0010;
|
||||
const STATUS_PSUBSCRIBED = 0x0100;
|
||||
|
||||
private $_redisClient, $_subscriptions, $_isStillValid, $_position;
|
||||
|
||||
public function __construct(Client $redisClient) {
|
||||
$this->_redisClient = $redisClient;
|
||||
$this->_isStillValid = true;
|
||||
$this->_subscriptions = false;
|
||||
$this->checkCapabilities($redisClient);
|
||||
$this->_redisClient = $redisClient;
|
||||
$this->_statusFlags = self::STATUS_VALID;
|
||||
}
|
||||
|
||||
public function __destruct() {
|
||||
@@ -884,9 +1014,28 @@ class PubSubContext implements \Iterator {
|
||||
}
|
||||
}
|
||||
|
||||
private function checkCapabilities(Client $redisClient) {
|
||||
if (Shared\Utils::isCluster($redisClient->getConnection())) {
|
||||
throw new \Predis\ClientException(
|
||||
'Cannot initialize a PUB/SUB context over a cluster of connections'
|
||||
);
|
||||
}
|
||||
$profile = $redisClient->getProfile();
|
||||
$commands = array('publish', 'subscribe', 'unsubscribe', 'psubscribe', 'punsubscribe');
|
||||
if ($profile->supportsCommands($commands) === false) {
|
||||
throw new \Predis\ClientException(
|
||||
'The current profile does not support PUB/SUB related commands'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private function isFlagSet($value) {
|
||||
return ($this->_statusFlags & $value) === $value;
|
||||
}
|
||||
|
||||
public function subscribe(/* arguments */) {
|
||||
$this->writeCommand(self::SUBSCRIBE, func_get_args());
|
||||
$this->_subscriptions = true;
|
||||
$this->_statusFlags |= self::STATUS_SUBSCRIBED;
|
||||
}
|
||||
|
||||
public function unsubscribe(/* arguments */) {
|
||||
@@ -895,7 +1044,7 @@ class PubSubContext implements \Iterator {
|
||||
|
||||
public function psubscribe(/* arguments */) {
|
||||
$this->writeCommand(self::PSUBSCRIBE, func_get_args());
|
||||
$this->_subscriptions = true;
|
||||
$this->_statusFlags |= self::STATUS_PSUBSCRIBED;
|
||||
}
|
||||
|
||||
public function punsubscribe(/* arguments */) {
|
||||
@@ -904,10 +1053,12 @@ class PubSubContext implements \Iterator {
|
||||
|
||||
public function closeContext() {
|
||||
if ($this->valid()) {
|
||||
// TODO: as an optimization, we should not send both
|
||||
// commands if one of them has not been issued.
|
||||
$this->unsubscribe();
|
||||
$this->punsubscribe();
|
||||
if ($this->isFlagSet(self::STATUS_SUBSCRIBED)) {
|
||||
$this->unsubscribe();
|
||||
}
|
||||
if ($this->isFlagSet(self::STATUS_PSUBSCRIBED)) {
|
||||
$this->punsubscribe();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -932,19 +1083,20 @@ class PubSubContext implements \Iterator {
|
||||
}
|
||||
|
||||
public function next() {
|
||||
if ($this->_isStillValid) {
|
||||
if ($this->isFlagSet(self::STATUS_VALID)) {
|
||||
$this->_position++;
|
||||
}
|
||||
return $this->_position;
|
||||
}
|
||||
|
||||
public function valid() {
|
||||
return $this->_subscriptions && $this->_isStillValid;
|
||||
$subscriptions = self::STATUS_SUBSCRIBED + self::STATUS_PSUBSCRIBED;
|
||||
return $this->isFlagSet(self::STATUS_VALID)
|
||||
&& ($this->_statusFlags & $subscriptions) > 0;
|
||||
}
|
||||
|
||||
private function invalidate() {
|
||||
$this->_isStillValid = false;
|
||||
$this->_subscriptions = false;
|
||||
$this->_statusFlags = 0x0000;
|
||||
}
|
||||
|
||||
private function getValue() {
|
||||
@@ -1381,6 +1533,15 @@ abstract class RedisServerProfile {
|
||||
return new $profile();
|
||||
}
|
||||
|
||||
public function supportsCommands(Array $commands) {
|
||||
foreach ($commands as $command) {
|
||||
if ($this->supportsCommand($command) === false) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public function supportsCommand($command) {
|
||||
return isset($this->_registeredCommands[$command]);
|
||||
}
|
||||
@@ -1658,6 +1819,13 @@ class RedisServer_v2_0 extends RedisServer_v1_2 {
|
||||
|
||||
class RedisServer_vNext extends RedisServer_v2_0 {
|
||||
public function getVersion() { return '2.1'; }
|
||||
public function getSupportedCommands() {
|
||||
return array_merge(parent::getSupportedCommands(), array(
|
||||
/* transactions */
|
||||
'watch' => '\Predis\Commands\Watch',
|
||||
'unwatch' => '\Predis\Commands\Unwatch',
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
@@ -1697,7 +1865,6 @@ class StandardExecutor implements IPipelineExecutor {
|
||||
|
||||
class SafeExecutor implements IPipelineExecutor {
|
||||
public function execute(\Predis\IConnection $connection, &$commands) {
|
||||
$firstServerException = null;
|
||||
$sizeofPipe = count($commands);
|
||||
$values = array();
|
||||
|
||||
@@ -1724,9 +1891,6 @@ class SafeExecutor implements IPipelineExecutor {
|
||||
$values[] = $exception->toResponseError();
|
||||
}
|
||||
catch (\Predis\CommunicationException $exception) {
|
||||
if ($throwExceptions) {
|
||||
throw $exception;
|
||||
}
|
||||
$toAdd = count($commands) - count($values);
|
||||
$values = array_merge($values, array_fill(0, $toAdd, $exception));
|
||||
break;
|
||||
@@ -1956,6 +2120,10 @@ class KetamaPureRing extends HashRing {
|
||||
namespace Predis\Shared;
|
||||
|
||||
class Utils {
|
||||
public static function isCluster(\Predis\IConnection $connection) {
|
||||
return $connection instanceof \Predis\ConnectionCluster;
|
||||
}
|
||||
|
||||
public static function onCommunicationException(\Predis\CommunicationException $exception) {
|
||||
if ($exception->shouldResetConnection()) {
|
||||
$connection = $exception->getConnection();
|
||||
@@ -2382,8 +2550,9 @@ class ZSetUnionStore extends \Predis\MultiBulkCommand {
|
||||
$finalizedOpts = array();
|
||||
if (isset($opts['WEIGHTS']) && is_array($opts['WEIGHTS'])) {
|
||||
$finalizedOpts[] = 'WEIGHTS';
|
||||
$finalizedOpts[] = $opts['WEIGHTS'][0];
|
||||
$finalizedOpts[] = $opts['WEIGHTS'][1];
|
||||
foreach ($opts['WEIGHTS'] as $weight) {
|
||||
$finalizedOpts[] = $weight;
|
||||
}
|
||||
}
|
||||
if (isset($opts['AGGREGATE'])) {
|
||||
$finalizedOpts[] = 'AGGREGATE';
|
||||
@@ -2398,20 +2567,42 @@ class ZSetIntersectionStore extends \Predis\Commands\ZSetUnionStore {
|
||||
}
|
||||
|
||||
class ZSetRange extends \Predis\MultiBulkCommand {
|
||||
private $_withScores = false;
|
||||
public function getCommandId() { return 'ZRANGE'; }
|
||||
public function parseResponse($data) {
|
||||
$arguments = $this->getArguments();
|
||||
public function filterArguments(Array $arguments) {
|
||||
if (count($arguments) === 4) {
|
||||
if (strtolower($arguments[3]) === 'withscores') {
|
||||
if ($data instanceof \Iterator) {
|
||||
return new \Predis\Shared\MultiBulkResponseKVIterator($data);
|
||||
}
|
||||
$result = array();
|
||||
for ($i = 0; $i < count($data); $i++) {
|
||||
$result[] = array($data[$i], $data[++$i]);
|
||||
}
|
||||
return $result;
|
||||
$lastType = gettype($arguments[3]);
|
||||
if ($lastType === 'string' && strtolower($arguments[3]) === 'withscores') {
|
||||
// used for compatibility with older versions
|
||||
$arguments[3] = array('WITHSCORES' => true);
|
||||
$lastType = 'array';
|
||||
}
|
||||
if ($lastType === 'array') {
|
||||
$options = $this->prepareOptions(array_pop($arguments));
|
||||
return array_merge($arguments, $options);
|
||||
}
|
||||
}
|
||||
return $arguments;
|
||||
}
|
||||
protected function prepareOptions($options) {
|
||||
$opts = array_change_key_case($options, CASE_UPPER);
|
||||
$finalizedOpts = array();
|
||||
if (isset($opts['WITHSCORES'])) {
|
||||
$finalizedOpts[] = 'WITHSCORES';
|
||||
$this->_withScores = true;
|
||||
}
|
||||
return $finalizedOpts;
|
||||
}
|
||||
public function parseResponse($data) {
|
||||
if ($this->_withScores) {
|
||||
if ($data instanceof \Iterator) {
|
||||
return new \Predis\Shared\MultiBulkResponseKVIterator($data);
|
||||
}
|
||||
$result = array();
|
||||
for ($i = 0; $i < count($data); $i++) {
|
||||
$result[] = array($data[$i], $data[++$i]);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
@@ -2423,6 +2614,17 @@ class ZSetReverseRange extends \Predis\Commands\ZSetRange {
|
||||
|
||||
class ZSetRangeByScore extends \Predis\Commands\ZSetRange {
|
||||
public function getCommandId() { return 'ZRANGEBYSCORE'; }
|
||||
protected function prepareOptions($options) {
|
||||
$opts = array_change_key_case($options, CASE_UPPER);
|
||||
$finalizedOpts = array();
|
||||
if (isset($opts['LIMIT']) && is_array($opts['LIMIT'])) {
|
||||
$limit = array_change_key_case($opts['LIMIT'], CASE_UPPER);
|
||||
$finalizedOpts[] = 'LIMIT';
|
||||
$finalizedOpts[] = isset($limit['OFFSET']) ? $limit['OFFSET'] : $limit[0];
|
||||
$finalizedOpts[] = isset($limit['COUNT']) ? $limit['COUNT'] : $limit[1];
|
||||
}
|
||||
return array_merge($finalizedOpts, parent::prepareOptions($options));
|
||||
}
|
||||
}
|
||||
|
||||
class ZSetCount extends \Predis\MultiBulkCommand {
|
||||
@@ -2626,6 +2828,18 @@ class Discard extends \Predis\MultiBulkCommand {
|
||||
public function getCommandId() { return 'DISCARD'; }
|
||||
}
|
||||
|
||||
class Watch extends \Predis\MultiBulkCommand {
|
||||
public function canBeHashed() { return false; }
|
||||
public function getCommandId() { return 'WATCH'; }
|
||||
public function parseResponse($data) { return (bool) $data; }
|
||||
}
|
||||
|
||||
class Unwatch extends \Predis\MultiBulkCommand {
|
||||
public function canBeHashed() { return false; }
|
||||
public function getCommandId() { return 'UNWATCH'; }
|
||||
public function parseResponse($data) { return (bool) $data; }
|
||||
}
|
||||
|
||||
/* publish/subscribe */
|
||||
class Subscribe extends \Predis\MultiBulkCommand {
|
||||
public function canBeHashed() { return false; }
|
||||
|
||||
@@ -1095,6 +1095,11 @@ class RedisCommandTestSuite extends PHPUnit_Framework_TestCase {
|
||||
$this->redis->zrange('zset', 0, 2, 'withscores')
|
||||
);
|
||||
|
||||
$this->assertEquals(
|
||||
array(array('a', -10), array('b', 0), array('c', 10)),
|
||||
$this->redis->zrange('zset', 0, 2, array('withscores' => true))
|
||||
);
|
||||
|
||||
RC::testForServerException($this, RC::EXCEPTION_WRONG_TYPE, function($test) {
|
||||
$test->redis->set('foo', 'bar');
|
||||
$test->redis->zrange('foo', 0, -1);
|
||||
@@ -1149,6 +1154,11 @@ class RedisCommandTestSuite extends PHPUnit_Framework_TestCase {
|
||||
$this->redis->zrevrange('zset', 0, 2, 'withscores')
|
||||
);
|
||||
|
||||
$this->assertEquals(
|
||||
array(array('f', 30), array('e', 20), array('d', 20)),
|
||||
$this->redis->zrevrange('zset', 0, 2, array('withscores' => true))
|
||||
);
|
||||
|
||||
RC::testForServerException($this, RC::EXCEPTION_WRONG_TYPE, function($test) {
|
||||
$test->redis->set('foo', 'bar');
|
||||
$test->redis->zrevrange('foo', 0, -1);
|
||||
@@ -1183,6 +1193,31 @@ class RedisCommandTestSuite extends PHPUnit_Framework_TestCase {
|
||||
$this->redis->zrangebyscore('zset', 10, 20, 'withscores')
|
||||
);
|
||||
|
||||
$this->assertEquals(
|
||||
array(array('c', 10), array('d', 20), array('e', 20)),
|
||||
$this->redis->zrangebyscore('zset', 10, 20, array('withscores' => true))
|
||||
);
|
||||
|
||||
$this->assertEquals(
|
||||
array('d', 'e'),
|
||||
$this->redis->zrangebyscore('zset', 10, 20, array('limit' => array(1, 2)))
|
||||
);
|
||||
|
||||
$this->assertEquals(
|
||||
array('d', 'e'),
|
||||
$this->redis->zrangebyscore('zset', 10, 20, array(
|
||||
'limit' => array('offset' => 1, 'count' => 2)
|
||||
))
|
||||
);
|
||||
|
||||
$this->assertEquals(
|
||||
array(array('d', 20), array('e', 20)),
|
||||
$this->redis->zrangebyscore('zset', 10, 20, array(
|
||||
'limit' => array(1, 2),
|
||||
'withscores' => true,
|
||||
))
|
||||
);
|
||||
|
||||
RC::testForServerException($this, RC::EXCEPTION_WRONG_TYPE, function($test) {
|
||||
$test->redis->set('foo', 'bar');
|
||||
$test->redis->zrangebyscore('foo', 0, 0);
|
||||
|
||||
Reference in New Issue
Block a user