From 2fc80a5f0d161b0ccac0decfc4455c729149c922 Mon Sep 17 00:00:00 2001 From: Daniele Alessandri Date: Sun, 28 Nov 2010 19:29:22 +0100 Subject: [PATCH] Backported changes from the mainline library to the PHP 5.2 branch (up to commit 975bd98) --- CHANGELOG | 22 ++++ lib/Predis.php | 186 +++++++++++++++++++++------------- test/PredisClientFeatures.php | 155 +++++++++++++++++++++++++++- test/PredisShared.php | 5 +- test/RedisCommandsTest.php | 165 +++++++++++++++++++++++++----- 5 files changed, 431 insertions(+), 102 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index ff6f43a4..d8777aa8 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,25 @@ +v0.6.2 (2010-xx-xx) + * Minor internal improvements and clean ups. + + * New commands available in the Redis v2.2 profile (dev): + - Strings: STRLEN + - Lists : LINSERT, RPUSHX, LPUSHX + - ZSets : ZREVRANGEBYSCORE + - Misc. : PERSIST + + * WATCH also accepts a single array parameter with the keys that should be + monitored during a transaction. + + * Improved the behaviour of Predis_MultiExecBlock in certain corner cases. + + * Improved parameters checking for the SORT command. + + * FIX: the STORE parameter for the SORT command didn't work correctly when + using '0' as the target key (ISSUE #13). + + * FIX: the methods for UNWATCH and DISCARD do not break anymore method + chaining with Predis\MultiExecBlock. + v0.6.1 (2010-07-11) * Minor internal improvements and clean ups. diff --git a/lib/Predis.php b/lib/Predis.php index 57d2b607..dc94c197 100644 --- a/lib/Predis.php +++ b/lib/Predis.php @@ -175,7 +175,7 @@ class Predis_Client { $newClient = new Predis_Client(); $newClient->setupClient($this->_options); - $newClient->setConnection($this->getConnection($connectionAlias)); + $newClient->setConnection($connection); return $newClient; } @@ -235,21 +235,25 @@ class Predis_Client { return $this->_connection->rawCommand($rawCommandData, $closesConnection); } - public function pipeline(/* arguments */) { - $argv = func_get_args(); - $argc = func_num_args(); - + private function sharedInitializer($argv, $initializer) { + $argc = count($argv); if ($argc === 0) { - return $this->initPipeline(); + return $this->$initializer(); } else if ($argc === 1) { list($arg0) = $argv; - return is_array($arg0) ? $this->initPipeline($arg0) : $this->initPipeline(null, $arg0); + return is_array($arg0) ? $this->$initializer($arg0) : $this->$initializer(null, $arg0); } else if ($argc === 2) { list($arg0, $arg1) = $argv; - return $this->initPipeline($arg0, $arg1); + return $this->$initializer($arg0, $arg1); } + return $this->$initializer($this, $arguments); + } + + public function pipeline(/* arguments */) { + $args = func_get_args(); + return $this->sharedInitializer($args, 'initPipeline'); } public function pipelineSafe($pipelineBlock = null) { @@ -281,20 +285,8 @@ class Predis_Client { } 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); - } + $args = func_get_args(); + return $this->sharedInitializer($args, 'initMultiExec'); } private function initMultiExec(Array $options = null, $transBlock = null) { @@ -475,12 +467,12 @@ abstract class Predis_Command { public function setArguments(/* arguments */) { $this->_arguments = $this->filterArguments(func_get_args()); - $this->_hash = null; + unset($this->_hash); } public function setArgumentsArray(Array $arguments) { $this->_arguments = $this->filterArguments($arguments); - $this->_hash = null; + unset($this->_hash); } public function getArguments() { @@ -938,45 +930,52 @@ class Predis_MultiExecBlock { $blockException = null; $returnValues = array(); - try { - if ($block !== null) { - $this->setInsideBlock(true); + if ($block !== null) { + $this->setInsideBlock(true); + try { $block($this); - $this->setInsideBlock(false); } - - if ($this->_discarded === true) { - return; + catch (Predis_CommunicationException $exception) { + $blockException = $exception; } - - $reply = $this->_redisClient->exec(); - if ($reply === null) { - throw new Predis_AbortedMultiExec('The current transaction has been aborted by the server'); + catch (Predis_ServerException $exception) { + $blockException = $exception; } - - $execReply = $reply instanceof Iterator ? iterator_to_array($reply) : $reply; - $commands = &$this->_commands; - $sizeofReplies = count($execReply); - - if ($sizeofReplies !== count($commands)) { - $this->malformedServerResponse('Unexpected number of responses for a MultiExecBlock'); + catch (Exception $exception) { + $blockException = $exception; + if ($this->_initialized === true) { + $this->discard(); + } } - - for ($i = 0; $i < $sizeofReplies; $i++) { - $returnValues[] = $commands[$i]->parseResponse($execReply[$i] instanceof Iterator - ? iterator_to_array($execReply[$i]) - : $execReply[$i] - ); - unset($commands[$i]); - } - } - catch (Exception $exception) { $this->setInsideBlock(false); - $blockException = $exception; + if ($blockException !== null) { + throw $blockException; + } } - if ($blockException !== null) { - throw $blockException; + if ($this->_initialized === false) { + return; + } + + $reply = $this->_redisClient->exec(); + if ($reply === null) { + throw new Predis_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); + + if ($sizeofReplies !== count($commands)) { + $this->malformedServerResponse('Unexpected number of responses for a Predis_MultiExecBlock'); + } + + for ($i = 0; $i < $sizeofReplies; $i++) { + $returnValues[] = $commands[$i]->parseResponse($execReply[$i] instanceof Iterator + ? iterator_to_array($execReply[$i]) + : $execReply[$i] + ); + unset($commands[$i]); } return $returnValues; @@ -1005,8 +1004,7 @@ class Predis_PubSubContext implements Iterator { public function __destruct() { if ($this->valid()) { - $this->_redisClient->unsubscribe(); - $this->_redisClient->punsubscribe(); + $this->closeContext(); } } @@ -1824,6 +1822,20 @@ class Predis_RedisServer_vNext extends Predis_RedisServer_v2_0 { /* transactions */ 'watch' => 'Predis_Commands_Watch', 'unwatch' => 'Predis_Commands_Unwatch', + + /* commands operating on string values */ + 'strlen' => 'Predis_Commands_Strlen', + + /* commands operating on the key space */ + 'persist' => 'Predis_Commands_Persist', + + /* commands operating on lists */ + 'rpushx' => 'Predis_Commands_ListPushTailX', + 'lpushx' => 'Predis_Commands_ListPushHeadX', + 'linsert' => 'Predis_Commands_ListInsert', + + /* commands operating on sorted sets */ + 'zrevrangebyscore' => 'Predis_Commands_ZSetReverseRangeByScore', )); } } @@ -2347,6 +2359,10 @@ class Predis_Commands_Substr extends Predis_MultiBulkCommand { public function getCommandId() { return 'SUBSTR'; } } +class Predis_Commands_Strlen extends Predis_MultiBulkCommand { + public function getCommandId() { return 'STRLEN'; } +} + /* commands operating on the key space */ class Predis_Commands_Keys extends Predis_MultiBulkCommand { public function canBeHashed() { return false; } @@ -2387,6 +2403,11 @@ class Predis_Commands_ExpireAt extends Predis_MultiBulkCommand { public function parseResponse($data) { return (bool) $data; } } +class Predis_Commands_Persist extends Predis_MultiBulkCommand { + public function getCommandId() { return 'PERSIST'; } + public function parseResponse($data) { return (bool) $data; } +} + class Predis_Commands_DatabaseSize extends Predis_MultiBulkCommand { public function canBeHashed() { return false; } public function getCommandId() { return 'DBSIZE'; } @@ -2401,10 +2422,18 @@ class Predis_Commands_ListPushTail extends Predis_MultiBulkCommand { public function getCommandId() { return 'RPUSH'; } } +class Predis_Commands_ListPushTailX extends Predis_MultiBulkCommand { + public function getCommandId() { return 'RPUSHX'; } +} + class Predis_Commands_ListPushHead extends Predis_MultiBulkCommand { public function getCommandId() { return 'LPUSH'; } } +class Predis_Commands_ListPushHeadX extends Predis_MultiBulkCommand { + public function getCommandId() { return 'LPUSHX'; } +} + class Predis_Commands_ListLength extends Predis_MultiBulkCommand { public function getCommandId() { return 'LLEN'; } } @@ -2453,6 +2482,10 @@ class Predis_Commands_ListPopLastBlocking extends Predis_MultiBulkCommand { public function getCommandId() { return 'BRPOP'; } } +class Predis_Commands_ListInsert extends Predis_MultiBulkCommand { + public function getCommandId() { return 'LINSERT'; } +} + /* commands operating on sets */ class Predis_Commands_SetAdd extends Predis_MultiBulkCommand { public function getCommandId() { return 'SADD'; } @@ -2623,6 +2656,10 @@ class Predis_Commands_ZSetRangeByScore extends Predis_Commands_ZSetRange { } } +class Predis_Commands_ZSetReverseRangeByScore extends Predis_Commands_ZSetRangeByScore { + public function getCommandId() { return 'ZREVRANGEBYSCORE'; } +} + class Predis_Commands_ZSetCount extends Predis_MultiBulkCommand { public function getCommandId() { return 'ZCOUNT'; } } @@ -2767,16 +2804,15 @@ class Predis_Commands_Sort extends Predis_MultiBulkCommand { return $arguments; } - // TODO: add more parameters checks $query = array($arguments[0]); - $sortParams = $arguments[1]; + $sortParams = array_change_key_case($arguments[1], CASE_UPPER); - if (isset($sortParams['by'])) { + if (isset($sortParams['BY'])) { $query[] = 'BY'; - $query[] = $sortParams['by']; + $query[] = $sortParams['BY']; } - if (isset($sortParams['get'])) { - $getargs = $sortParams['get']; + if (isset($sortParams['GET'])) { + $getargs = $sortParams['GET']; if (is_array($getargs)) { foreach ($getargs as $getarg) { $query[] = 'GET'; @@ -2788,20 +2824,22 @@ class Predis_Commands_Sort extends Predis_MultiBulkCommand { $query[] = $getargs; } } - if (isset($sortParams['limit']) && is_array($sortParams['limit'])) { + if (isset($sortParams['LIMIT']) && is_array($sortParams['LIMIT']) + && count($sortParams['LIMIT']) == 2) { + $query[] = 'LIMIT'; - $query[] = $sortParams['limit'][0]; - $query[] = $sortParams['limit'][1]; + $query[] = $sortParams['LIMIT'][0]; + $query[] = $sortParams['LIMIT'][1]; } - if (isset($sortParams['sort'])) { - $query[] = strtoupper($sortParams['sort']); + if (isset($sortParams['SORT'])) { + $query[] = strtoupper($sortParams['SORT']); } - if (isset($sortParams['alpha']) && $sortParams['alpha'] == true) { + if (isset($sortParams['ALPHA']) && $sortParams['ALPHA'] == true) { $query[] = 'ALPHA'; } - if (isset($sortParams['store']) && $sortParams['store'] == true) { + if (isset($sortParams['STORE'])) { $query[] = 'STORE'; - $query[] = $sortParams['store']; + $query[] = $sortParams['STORE']; } return $query; @@ -2853,6 +2891,12 @@ class Predis_Commands_Publish extends Predis_MultiBulkCommand { class Predis_Commands_Watch extends Predis_MultiBulkCommand { public function canBeHashed() { return false; } public function getCommandId() { return 'WATCH'; } + public function filterArguments(Array $arguments) { + if (isset($arguments[0]) && is_array($arguments[0])) { + return $arguments[0]; + } + return $arguments; + } public function parseResponse($data) { return (bool) $data; } } diff --git a/test/PredisClientFeatures.php b/test/PredisClientFeatures.php index ce7db2d0..841c2baf 100644 --- a/test/PredisClientFeatures.php +++ b/test/PredisClientFeatures.php @@ -260,11 +260,15 @@ class PredisClientFeaturesTestSuite extends PHPUnit_Framework_TestCase { function testConnection_WriteCommandAndCloseConnection() { $cmd = Predis_RedisServerProfile::getDefault()->createCommand('quit'); - $connection = new Predis_Connection(RC::getConnectionParameters()); - $connection->connect(); + $connection = new Predis_Connection(new Predis_ConnectionParameters( + RC::getConnectionArguments() + array('read_write_timeout' => 0.5) + )); + $connection->connect(); $this->assertTrue($connection->isConnected()); $connection->writeCommand($cmd); + $connection->disconnect(); + $expectedMessage = 'Error while reading line from the server'; $thrownException = null; try { @@ -275,7 +279,6 @@ class PredisClientFeaturesTestSuite extends PHPUnit_Framework_TestCase { } $this->assertType('Predis_CommunicationException', $thrownException); $this->assertEquals($expectedMessage, $thrownException->getMessage()); - //$this->assertFalse($connection->isConnected()); } function testConnection_GetSocketOpensConnection() { @@ -370,8 +373,8 @@ class PredisClientFeaturesTestSuite extends PHPUnit_Framework_TestCase { function testResponseReader_OptionExceptionOnError() { $connection = new Predis_Connection(RC::getConnectionParameters()); $responseReader = $connection->getResponseReader(); - $connection->rawCommand("SET key 5\r\nvalue\r\n"); - $rawCmdUnexpected = "LPUSH key 5\r\nvalue\r\n"; + $connection->rawCommand("*3\r\n$3\r\nSET\r\n$3\r\nkey\r\n$5\r\nvalue\r\n"); + $rawCmdUnexpected = "*3\r\n$5\r\nLPUSH\r\n$3\r\nkey\r\n$5\r\nvalue\r\n"; $responseReader->setHandler( Predis_Protocol::PREFIX_ERROR, @@ -499,5 +502,147 @@ class PredisClientFeaturesTestSuite extends PHPUnit_Framework_TestCase { $this->assertEquals('bar', $replies[3][0]); $this->assertEquals('piyo', $replies[3][1]); } + + + /* Client + MultiExecBlock */ + + function testMultiExecBlock_Simple() { + $client = RC::getConnection(); + $client->flushdb(); + + $multi = $client->multiExec(); + + $this->assertType('Predis_MultiExecBlock', $multi); + $this->assertType('Predis_MultiExecBlock', $multi->set('foo', 'bar')); + $this->assertType('Predis_MultiExecBlock', $multi->set('hoge', 'piyo')); + $this->assertType('Predis_MultiExecBlock', $multi->mset(array( + 'foofoo' => 'barbar', 'hogehoge' => 'piyopiyo' + ))); + $this->assertType('Predis_MultiExecBlock', $multi->mget(array( + 'foo', 'hoge', 'foofoo', 'hogehoge' + ))); + + $replies = $multi->execute(); + $this->assertType('array', $replies); + $this->assertEquals(4, count($replies)); + $this->assertEquals(4, count($replies[3])); + $this->assertEquals('barbar', $replies[3][2]); + } + + function testMultiExecBlock_FluentInterface() { + $client = RC::getConnection(); + $client->flushdb(); + + $replies = $client->multiExec()->ping()->set('foo', 'bar')->get('foo')->execute(); + $this->assertType('array', $replies); + $this->assertEquals('bar', $replies[2]); + } + + function testMultiExecBlock_CallableAnonymousBlock() { + $client = RC::getConnection(); + $client->flushdb(); + + $replies = $client->multiExec(p_anon("\$multi", " + \$multi->ping(); + \$multi->set('foo', 'bar'); + \$multi->get('foo'); + ")); + + $this->assertType('array', $replies); + $this->assertEquals('bar', $replies[2]); + } + + function testMultiExecBlock_EmptyCallableBlock() { + $client = RC::getConnection(); + $client->flushdb(); + + $replies = $client->multiExec(p_anon("\$multi", "")); + + $this->assertEquals(0, count($replies)); + } + + function testMultiExecBlock_ClientExceptionInCallableBlock() { + $client = RC::getConnection(); + $client->flushdb(); + + $expectedMessage = 'TEST'; + $thrownException = null; + try { + $client->multiExec(p_anon("\$multi", " + \$multi->ping(); + \$multi->set('foo', 'bar'); + throw new Predis_ClientException('$expectedMessage'); + ")); + } + catch (Predis_ClientException $exception) { + $thrownException = $exception; + } + $this->assertType('Predis_ClientException', $thrownException); + $this->assertEquals($expectedMessage, $thrownException->getMessage()); + + $this->assertFalse($client->exists('foo')); + } + + function testMultiExecBlock_ServerExceptionInCallableBlock() { + $client = RC::getConnection(); + $client->flushdb(); + $client->getResponseReader()->setHandler('-', new Predis_ResponseErrorSilentHandler()); + + $multi = $client->multiExec(); + $multi->set('foo', 'bar'); + $multi->lpush('foo', 'piyo'); // LIST operation on STRING type returns an ERROR + $multi->set('hoge', 'piyo'); + $replies = $multi->execute(); + + $this->assertType('array', $replies); + $this->assertType('Predis_ResponseError', $replies[1]); + $this->assertTrue($client->exists('foo')); + $this->assertTrue($client->exists('hoge')); + } + + function testMultiExecBlock_Discard() { + $client = RC::getConnection(); + $client->flushdb(); + + $multi = $client->multiExec(); + $multi->set('foo', 'bar'); + $multi->discard(); + $multi->set('hoge', 'piyo'); + $replies = $multi->execute(); + + $this->assertEquals(1, count($replies)); + $this->assertFalse($client->exists('foo')); + $this->assertTrue($client->exists('hoge')); + } + + function testMultiExecBlock_DiscardEmpty() { + $client = RC::getConnection(); + $client->flushdb(); + + $replies = $client->multiExec()->discard()->execute(); + $this->assertEquals(0, count($replies)); + } + + function testMultiExecBlock_Watch() { + $client1 = RC::getConnection(); + $client2 = RC::getConnection(true); + $client1->flushdb(); + + $thrownException = null; + try { + $multi = $client1->multiExec(array('watch' => 'sentinel')); + $multi->set('sentinel', 'client1'); + $multi->get('sentinel'); + $client2->set('sentinel', 'client2'); + $multi->execute(); + } + catch (PredisException $exception) { + $thrownException = $exception; + } + $this->assertType('Predis_AbortedMultiExec', $thrownException); + $this->assertEquals('The current transaction has been aborted by the server', $thrownException->getMessage()); + + $this->assertEquals('client2', $client1->get('sentinel')); + } } ?> diff --git a/test/PredisShared.php b/test/PredisShared.php index 1f76b420..39d263e2 100644 --- a/test/PredisShared.php +++ b/test/PredisShared.php @@ -48,7 +48,10 @@ class RC { return $connection; } - public static function getConnection() { + public static function getConnection($new = false) { + if ($new == true) { + return self::createConnection(); + } if (self::$_connection === null || !self::$_connection->isConnected()) { self::$_connection = self::createConnection(); } diff --git a/test/RedisCommandsTest.php b/test/RedisCommandsTest.php index 9fe8ca49..7aa66a00 100644 --- a/test/RedisCommandsTest.php +++ b/test/RedisCommandsTest.php @@ -241,6 +241,18 @@ class RedisCommandTestSuite extends PHPUnit_Framework_TestCase { ")); } + function testStrlen() { + $this->redis->set('var', 'foobar'); + $this->assertEquals(6, $this->redis->strlen('var')); + $this->assertEquals(9, $this->redis->append('var', '___')); + $this->assertEquals(9, $this->redis->strlen('var')); + + RC::testForServerException($this, RC::EXCEPTION_WRONG_TYPE, p_anon("\$test", " + \$test->redis->rpush('metavars', 'foo'); + \$test->redis->strlen('metavars'); + ")); + } + /* commands operating on the key space */ @@ -369,6 +381,20 @@ class RedisCommandTestSuite extends PHPUnit_Framework_TestCase { ")); } + function testPushTailX() { + $this->assertEquals(0, $this->redis->rpushx('numbers', 1)); + $this->assertEquals(1, $this->redis->rpush('numbers', 2)); + $this->assertEquals(2, $this->redis->rpushx('numbers', 3)); + + $this->assertEquals(2, $this->redis->llen('numbers')); + $this->assertEquals(array(2, 3), $this->redis->lrange('numbers', 0, -1)); + + RC::testForServerException($this, RC::EXCEPTION_WRONG_TYPE, p_anon("\$test", " + \$test->redis->set('foo', 'bar'); + \$test->redis->rpushx('foo', 'bar'); + ")); + } + function testPushHead() { // NOTE: List push operations return the list length since Redis commit 520b5a3 $this->assertEquals(1, $this->redis->lpush('metavars', 'foo')); @@ -382,6 +408,20 @@ class RedisCommandTestSuite extends PHPUnit_Framework_TestCase { ")); } + function testPushHeadX() { + $this->assertEquals(0, $this->redis->lpushx('numbers', 1)); + $this->assertEquals(1, $this->redis->lpush('numbers', 2)); + $this->assertEquals(2, $this->redis->lpushx('numbers', 3)); + + $this->assertEquals(2, $this->redis->llen('numbers')); + $this->assertEquals(array(3, 2), $this->redis->lrange('numbers', 0, -1)); + + RC::testForServerException($this, RC::EXCEPTION_WRONG_TYPE, p_anon("\$test", " + \$test->redis->set('foo', 'bar'); + \$test->redis->lpushx('foo', 'bar'); + ")); + } + function testListLength() { $this->assertEquals(1, $this->redis->rpush('metavars', 'foo')); $this->assertEquals(2, $this->redis->rpush('metavars', 'hoge')); @@ -682,6 +722,22 @@ class RedisCommandTestSuite extends PHPUnit_Framework_TestCase { $this->assertEquals((float)(time() - $start), 2, '', 1); } + function testListInsert() { + $numbers = RC::pushTailAndReturn($this->redis, 'numbers', RC::getArrayOfNumbers()); + + $this->assertEquals(11, $this->redis->linsert('numbers', 'before', 0, -2)); + $this->assertEquals(12, $this->redis->linsert('numbers', 'after', -2, -1)); + $this->assertEquals(array(-2, -1, 0, 1), $this->redis->lrange('numbers', 0, 3)); + + $this->assertEquals(-1, $this->redis->linsert('numbers', 'after', 100, 200)); + $this->assertEquals(-1, $this->redis->linsert('numbers', 'before', 100, 50)); + + RC::testForServerException($this, RC::EXCEPTION_WRONG_TYPE, p_anon("\$test", " + \$test->redis->set('foo', 'bar'); + \$test->redis->lset('foo', 0, 0); + ")); + } + /* commands operating on sets */ @@ -1161,31 +1217,6 @@ class RedisCommandTestSuite extends PHPUnit_Framework_TestCase { $this->redis->zrevrange('zset', 0, 2, array('withscores' => true)) ); - $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, p_anon("\$test", " \$test->redis->set('foo', 'bar'); \$test->redis->zrevrange('foo', 0, -1); @@ -1220,12 +1251,96 @@ 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, p_anon("\$test", " \$test->redis->set('foo', 'bar'); \$test->redis->zrangebyscore('foo', 0, 0); ")); } + function testZsetReverseRangeByScore() { + $zset = RC::zsetAddAndReturn($this->redis, 'zset', RC::getZSetArray()); + + $this->assertEquals( + array('a'), + $this->redis->zrevrangebyscore('zset', -10, -10) + ); + + $this->assertEquals( + array('b', 'a'), + $this->redis->zrevrangebyscore('zset', 0, -10) + ); + + $this->assertEquals( + array('e', 'd'), + $this->redis->zrevrangebyscore('zset', 20, 20) + ); + + $this->assertEquals( + array('f', 'e', 'd', 'c', 'b'), + $this->redis->zrevrangebyscore('zset', 30, 0) + ); + + $this->assertEquals( + array(array('e', 20), array('d', 20), array('c', 10)), + $this->redis->zrevrangebyscore('zset', 20, 10, 'withscores') + ); + + $this->assertEquals( + array(array('e', 20), array('d', 20), array('c', 10)), + $this->redis->zrevrangebyscore('zset', 20, 10, array('withscores' => true)) + ); + + $this->assertEquals( + array('d', 'c'), + $this->redis->zrevrangebyscore('zset', 20, 10, array('limit' => array(1, 2))) + ); + + $this->assertEquals( + array('d', 'c'), + $this->redis->zrevrangebyscore('zset', 20, 10, array( + 'limit' => array('offset' => 1, 'count' => 2) + )) + ); + + $this->assertEquals( + array(array('d', 20), array('c', 10)), + $this->redis->zrevrangebyscore('zset', 20, 10, array( + 'limit' => array(1, 2), + 'withscores' => true, + )) + ); + + RC::testForServerException($this, RC::EXCEPTION_WRONG_TYPE, p_anon("\$test", " + \$test->redis->set('foo', 'bar'); + \$test->redis->zrevrangebyscore('foo', 0, 0); + ")); + } + function testZsetUnionStore() { $zsetA = RC::zsetAddAndReturn($this->redis, 'zseta', array('a' => 1, 'b' => 2, 'c' => 3)); $zsetB = RC::zsetAddAndReturn($this->redis, 'zsetb', array('b' => 1, 'c' => 2, 'd' => 3));