瀏覽代碼

Backported changes from the mainline library to the PHP 5.2 branch (up to commit 17f83a5)

Daniele Alessandri 14 年之前
父節點
當前提交
ada8878295
共有 12 個文件被更改,包括 1061 次插入382 次删除
  1. 36 0
      CHANGELOG
  2. 1 0
      README.markdown
  3. 1 1
      VERSION
  4. 1 1
      examples/CustomDistributionStrategy.php
  5. 1 1
      examples/PubSubContext.php
  6. 585 281
      lib/Predis.php
  7. 239 0
      lib/Predis_Compatibility.php
  8. 15 0
      phpunit.xml.dist
  9. 103 76
      test/ClientFeaturesTest.php
  10. 22 10
      test/PredisShared.php
  11. 48 12
      test/RedisCommandsTest.php
  12. 9 0
      test/bootstrap.php

+ 36 - 0
CHANGELOG

@@ -1,3 +1,39 @@
+v0.6.6 (2011-xx-xx)
+  * Switched to Redis 2.2 as the default server profile (there are no changes 
+    that would break compatibility with previous releases). Long command names 
+    are no more supported by default but if you need them you can still require 
+    Predis_Compatibility.php to avoid breaking compatibility.
+
+  * Added a VERSION constant to Predis\Client.
+
+  * Some performance improvements for multibulk replies (parsing them is about 
+    16% faster than the previous version). A few core classes have been heavily 
+    optimized to reduce overhead when creating new instances.
+
+  * Predis now uses by default a new protocol reader, more lightweight and 
+    faster than the default handler-based one. Users can revert to the old 
+    protocol reader with the 'reader' client option set to 'composable'.
+    This client option can also accept custom reader classes implementing the 
+    new Predis\IResponseReader interface.
+
+  * Added support for connecting to Redis using UNIX domain sockets (ISSUE #25).
+
+  * The "read_write_timeout" connection parameter can now be set to 0 or false 
+    to disable read and write timeouts on connections. The old behaviour of -1 
+    is still intact.
+
+  * ZUNIONSTORE and ZINTERSTORE can accept an array to specify a list of the 
+    source keys to be used to populate the destination key.
+
+  * MGET, SINTER, SUNION and SDIFF can accept an array to specify the list of 
+    keys. SINTERSTORE, SUNIONSTORE and SDIFFSTORE can also accept an array to 
+    specify the list of source keys.
+
+  * SUBSCRIBE and PSUBSCRIBE can accept a list of channels for subscription.
+
+  * FIX: some client-side clean-ups for MULTI/EXEC were handled incorrectly in 
+    a couple of corner cases. See also ISSUE #27.
+
 v0.6.5 (2011-02-12)
   * FIX: due to an untested internal change introduced in v0.6.4, a wrong
     handling of bulk reads of zero-length values was producing protocol

+ 1 - 0
README.markdown

@@ -22,6 +22,7 @@ to be implemented soon in Predis.
 - Command pipelining on single and multiple connections (transparent).
 - Abstraction for Redis transactions (>= 2.0) with support for CAS operations (>= 2.2).
 - Lazy connections (connections to Redis instances are only established just in time).
+- Ability to connect to Redis using TCP/IP or UNIX domain sockets.
 - Flexible system to define and register your own set of commands to a client instance.
 
 

+ 1 - 1
VERSION

@@ -1 +1 @@
-0.6.5
+0.6.6-dev

+ 1 - 1
examples/CustomDistributionStrategy.php

@@ -3,7 +3,7 @@ require_once 'SharedConfigurations.php';
 
 // Developers can customize the distribution strategy used by the client 
 // to distribute keys among a cluster of servers simply by creating a class 
-// that implements the Predis_Distribution_IDistributionAlgorithm interface.
+// that implements the Predis_Distribution_IDistributionStrategy interface.
 
 class NaiveDistributionStrategy
     implements Predis_Distribution_IDistributionStrategy {

+ 1 - 1
examples/PubSubContext.php

@@ -5,7 +5,7 @@ require_once 'SharedConfigurations.php';
 // events published on certain channels (PUBSUB).
 
 // Create a client and disable r/w timeout on the socket
-$redis  = new Predis_Client($single_server + array('read_write_timeout' => -1));
+$redis  = new Predis_Client($single_server + array('read_write_timeout' => 0));
 
 // Initialize a new pubsub context
 $pubsub = $redis->pubSubContext();

文件差異過大導致無法顯示
+ 585 - 281
lib/Predis.php


+ 239 - 0
lib/Predis_Compatibility.php

@@ -1,5 +1,8 @@
 <?php
+
 Predis_RedisServerProfile::registerProfile('Predis_RedisServer_v1_0', '1.0');
+Predis_RedisServerProfile::registerProfile('Predis_RedisServer_v1_2_LongNames', '1.2');
+Predis_RedisServerProfile::registerProfile('Predis_RedisServer_v2_0_LongNames', '2.0');
 
 class Predis_RedisServer_v1_0 extends Predis_RedisServerProfile {
     public function getVersion() { return '1.0'; }
@@ -131,6 +134,242 @@ class Predis_RedisServer_v1_0 extends Predis_RedisServerProfile {
     }
 }
 
+class Predis_RedisServer_v1_2_LongNames extends Predis_RedisServerProfile {
+    public function getVersion() { return '1.2'; }
+    public function getSupportedCommands() {
+        return array(
+            /* miscellaneous commands */
+            'ping'      => 'Predis_Commands_Ping',
+            'echo'      => 'Predis_Commands_DoEcho',
+            'auth'      => 'Predis_Commands_Auth',
+
+            /* connection handling */
+            'quit'      => 'Predis_Commands_Quit',
+
+            /* commands operating on string values */
+            'set'                     => 'Predis_Commands_Set',
+            'setnx'                   => 'Predis_Commands_SetPreserve',
+                'setPreserve'         => 'Predis_Commands_SetPreserve',
+            'mset'                    => 'Predis_Commands_SetMultiple',
+                'setMultiple'         => 'Predis_Commands_SetMultiple',
+            'msetnx'                  => 'Predis_Commands_SetMultiplePreserve',
+                'setMultiplePreserve' => 'Predis_Commands_SetMultiplePreserve',
+            'get'                     => 'Predis_Commands_Get',
+            'mget'                    => 'Predis_Commands_GetMultiple',
+                'getMultiple'         => 'Predis_Commands_GetMultiple',
+            'getset'                  => 'Predis_Commands_GetSet',
+                'getSet'              => 'Predis_Commands_GetSet',
+            'incr'                    => 'Predis_Commands_Increment',
+                'increment'           => 'Predis_Commands_Increment',
+            'incrby'                  => 'Predis_Commands_IncrementBy',
+                'incrementBy'         => 'Predis_Commands_IncrementBy',
+            'decr'                    => 'Predis_Commands_Decrement',
+                'decrement'           => 'Predis_Commands_Decrement',
+            'decrby'                  => 'Predis_Commands_DecrementBy',
+                'decrementBy'         => 'Predis_Commands_DecrementBy',
+            'exists'                  => 'Predis_Commands_Exists',
+            'del'                     => 'Predis_Commands_Delete',
+                'delete'              => 'Predis_Commands_Delete',
+            'type'                    => 'Predis_Commands_Type',
+
+            /* commands operating on the key space */
+            'keys'               => 'Predis_Commands_Keys_v1_2',
+            'randomkey'          => 'Predis_Commands_RandomKey',
+                'randomKey'      => 'Predis_Commands_RandomKey',
+            'rename'             => 'Predis_Commands_Rename',
+            'renamenx'           => 'Predis_Commands_RenamePreserve',
+                'renamePreserve' => 'Predis_Commands_RenamePreserve',
+            'expire'             => 'Predis_Commands_Expire',
+            'expireat'           => 'Predis_Commands_ExpireAt',
+                'expireAt'       => 'Predis_Commands_ExpireAt',
+            'dbsize'             => 'Predis_Commands_DatabaseSize',
+                'databaseSize'   => 'Predis_Commands_DatabaseSize',
+            'ttl'                => 'Predis_Commands_TimeToLive',
+                'timeToLive'     => 'Predis_Commands_TimeToLive',
+
+            /* commands operating on lists */
+            'rpush'            => 'Predis_Commands_ListPushTail',
+                'pushTail'     => 'Predis_Commands_ListPushTail',
+            'lpush'            => 'Predis_Commands_ListPushHead',
+                'pushHead'     => 'Predis_Commands_ListPushHead',
+            'llen'             => 'Predis_Commands_ListLength',
+                'listLength'   => 'Predis_Commands_ListLength',
+            'lrange'           => 'Predis_Commands_ListRange',
+                'listRange'    => 'Predis_Commands_ListRange',
+            'ltrim'            => 'Predis_Commands_ListTrim',
+                'listTrim'     => 'Predis_Commands_ListTrim',
+            'lindex'           => 'Predis_Commands_ListIndex',
+                'listIndex'    => 'Predis_Commands_ListIndex',
+            'lset'             => 'Predis_Commands_ListSet',
+                'listSet'      => 'Predis_Commands_ListSet',
+            'lrem'             => 'Predis_Commands_ListRemove',
+                'listRemove'   => 'Predis_Commands_ListRemove',
+            'lpop'             => 'Predis_Commands_ListPopFirst',
+                'popFirst'     => 'Predis_Commands_ListPopFirst',
+            'rpop'             => 'Predis_Commands_ListPopLast',
+                'popLast'      => 'Predis_Commands_ListPopLast',
+            'rpoplpush'        => 'Predis_Commands_ListPopLastPushHead',
+                'listPopLastPushHead'  => 'Predis_Commands_ListPopLastPushHead',
+
+            /* commands operating on sets */
+            'sadd'                      => 'Predis_Commands_SetAdd',
+                'setAdd'                => 'Predis_Commands_SetAdd',
+            'srem'                      => 'Predis_Commands_SetRemove',
+                'setRemove'             => 'Predis_Commands_SetRemove',
+            'spop'                      => 'Predis_Commands_SetPop',
+                'setPop'                => 'Predis_Commands_SetPop',
+            'smove'                     => 'Predis_Commands_SetMove',
+                'setMove'               => 'Predis_Commands_SetMove',
+            'scard'                     => 'Predis_Commands_SetCardinality',
+                'setCardinality'        => 'Predis_Commands_SetCardinality',
+            'sismember'                 => 'Predis_Commands_SetIsMember',
+                'setIsMember'           => 'Predis_Commands_SetIsMember',
+            'sinter'                    => 'Predis_Commands_SetIntersection',
+                'setIntersection'       => 'Predis_Commands_SetIntersection',
+            'sinterstore'               => 'Predis_Commands_SetIntersectionStore',
+                'setIntersectionStore'  => 'Predis_Commands_SetIntersectionStore',
+            'sunion'                    => 'Predis_Commands_SetUnion',
+                'setUnion'              => 'Predis_Commands_SetUnion',
+            'sunionstore'               => 'Predis_Commands_SetUnionStore',
+                'setUnionStore'         => 'Predis_Commands_SetUnionStore',
+            'sdiff'                     => 'Predis_Commands_SetDifference',
+                'setDifference'         => 'Predis_Commands_SetDifference',
+            'sdiffstore'                => 'Predis_Commands_SetDifferenceStore',
+                'setDifferenceStore'    => 'Predis_Commands_SetDifferenceStore',
+            'smembers'                  => 'Predis_Commands_SetMembers',
+                'setMembers'            => 'Predis_Commands_SetMembers',
+            'srandmember'               => 'Predis_Commands_SetRandomMember',
+                'setRandomMember'       => 'Predis_Commands_SetRandomMember',
+
+            /* commands operating on sorted sets */
+            'zadd'                          => 'Predis_Commands_ZSetAdd',
+                'zsetAdd'                   => 'Predis_Commands_ZSetAdd',
+            'zincrby'                       => 'Predis_Commands_ZSetIncrementBy',
+                'zsetIncrementBy'           => 'Predis_Commands_ZSetIncrementBy',
+            'zrem'                          => 'Predis_Commands_ZSetRemove',
+                'zsetRemove'                => 'Predis_Commands_ZSetRemove',
+            'zrange'                        => 'Predis_Commands_ZSetRange',
+                'zsetRange'                 => 'Predis_Commands_ZSetRange',
+            'zrevrange'                     => 'Predis_Commands_ZSetReverseRange',
+                'zsetReverseRange'          => 'Predis_Commands_ZSetReverseRange',
+            'zrangebyscore'                 => 'Predis_Commands_ZSetRangeByScore',
+                'zsetRangeByScore'          => 'Predis_Commands_ZSetRangeByScore',
+            'zcard'                         => 'Predis_Commands_ZSetCardinality',
+                'zsetCardinality'           => 'Predis_Commands_ZSetCardinality',
+            'zscore'                        => 'Predis_Commands_ZSetScore',
+                'zsetScore'                 => 'Predis_Commands_ZSetScore',
+            'zremrangebyscore'              => 'Predis_Commands_ZSetRemoveRangeByScore',
+                'zsetRemoveRangeByScore'    => 'Predis_Commands_ZSetRemoveRangeByScore',
+
+            /* multiple databases handling commands */
+            'select'                => 'Predis_Commands_SelectDatabase',
+                'selectDatabase'    => 'Predis_Commands_SelectDatabase',
+            'move'                  => 'Predis_Commands_MoveKey',
+                'moveKey'           => 'Predis_Commands_MoveKey',
+            'flushdb'               => 'Predis_Commands_FlushDatabase',
+                'flushDatabase'     => 'Predis_Commands_FlushDatabase',
+            'flushall'              => 'Predis_Commands_FlushAll',
+                'flushDatabases'    => 'Predis_Commands_FlushAll',
+
+            /* sorting */
+            'sort'                  => 'Predis_Commands_Sort',
+
+            /* remote server control commands */
+            'info'                  => 'Predis_Commands_Info',
+            'slaveof'               => 'Predis_Commands_SlaveOf',
+                'slaveOf'           => 'Predis_Commands_SlaveOf',
+
+            /* persistence control commands */
+            'save'                  => 'Predis_Commands_Save',
+            'bgsave'                => 'Predis_Commands_BackgroundSave',
+                'backgroundSave'    => 'Predis_Commands_BackgroundSave',
+            'lastsave'              => 'Predis_Commands_LastSave',
+                'lastSave'          => 'Predis_Commands_LastSave',
+            'shutdown'              => 'Predis_Commands_Shutdown',
+            'bgrewriteaof'                      =>  'Predis_Commands_BackgroundRewriteAppendOnlyFile',
+            'backgroundRewriteAppendOnlyFile'   =>  'Predis_Commands_BackgroundRewriteAppendOnlyFile',
+        );
+    }
+}
+
+class Predis_RedisServer_v2_0_LongNames extends Predis_RedisServer_v1_2_LongNames {
+    public function getVersion() { return '2.0'; }
+    public function getSupportedCommands() {
+        return array_merge(parent::getSupportedCommands(), array(
+            /* transactions */
+            'multi'                     => 'Predis_Commands_Multi',
+            'exec'                      => 'Predis_Commands_Exec',
+            'discard'                   => 'Predis_Commands_Discard',
+
+            /* commands operating on string values */
+            'setex'                     => 'Predis_Commands_SetExpire',
+                'setExpire'             => 'Predis_Commands_SetExpire',
+            'append'                    => 'Predis_Commands_Append',
+            'substr'                    => 'Predis_Commands_Substr',
+
+            /* commands operating on the key space */
+            'keys'                      => 'Predis_Commands_Keys',
+
+            /* commands operating on lists */
+            'blpop'                     => 'Predis_Commands_ListPopFirstBlocking',
+                'popFirstBlocking'      => 'Predis_Commands_ListPopFirstBlocking',
+            'brpop'                     => 'Predis_Commands_ListPopLastBlocking',
+                'popLastBlocking'       => 'Predis_Commands_ListPopLastBlocking',
+
+            /* commands operating on sorted sets */
+            'zunionstore'               => 'Predis_Commands_ZSetUnionStore',
+                'zsetUnionStore'        => 'Predis_Commands_ZSetUnionStore',
+            'zinterstore'               => 'Predis_Commands_ZSetIntersectionStore',
+                'zsetIntersectionStore' => 'Predis_Commands_ZSetIntersectionStore',
+            'zcount'                    => 'Predis_Commands_ZSetCount',
+                'zsetCount'             => 'Predis_Commands_ZSetCount',
+            'zrank'                     => 'Predis_Commands_ZSetRank',
+                'zsetRank'              => 'Predis_Commands_ZSetRank',
+            'zrevrank'                  => 'Predis_Commands_ZSetReverseRank',
+                'zsetReverseRank'       => 'Predis_Commands_ZSetReverseRank',
+            'zremrangebyrank'           => 'Predis_Commands_ZSetRemoveRangeByRank',
+                'zsetRemoveRangeByRank' => 'Predis_Commands_ZSetRemoveRangeByRank',
+
+            /* commands operating on hashes */
+            'hset'                      => 'Predis_Commands_HashSet',
+                'hashSet'               => 'Predis_Commands_HashSet',
+            'hsetnx'                    => 'Predis_Commands_HashSetPreserve',
+                'hashSetPreserve'       => 'Predis_Commands_HashSetPreserve',
+            'hmset'                     => 'Predis_Commands_HashSetMultiple',
+                'hashSetMultiple'       => 'Predis_Commands_HashSetMultiple',
+            'hincrby'                   => 'Predis_Commands_HashIncrementBy',
+                'hashIncrementBy'       => 'Predis_Commands_HashIncrementBy',
+            'hget'                      => 'Predis_Commands_HashGet',
+                'hashGet'               => 'Predis_Commands_HashGet',
+            'hmget'                     => 'Predis_Commands_HashGetMultiple',
+                'hashGetMultiple'       => 'Predis_Commands_HashGetMultiple',
+            'hdel'                      => 'Predis_Commands_HashDelete',
+                'hashDelete'            => 'Predis_Commands_HashDelete',
+            'hexists'                   => 'Predis_Commands_HashExists',
+                'hashExists'            => 'Predis_Commands_HashExists',
+            'hlen'                      => 'Predis_Commands_HashLength',
+                'hashLength'            => 'Predis_Commands_HashLength',
+            'hkeys'                     => 'Predis_Commands_HashKeys',
+                'hashKeys'              => 'Predis_Commands_HashKeys',
+            'hvals'                     => 'Predis_Commands_HashValues',
+                'hashValues'            => 'Predis_Commands_HashValues',
+            'hgetall'                   => 'Predis_Commands_HashGetAll',
+                'hashGetAll'            => 'Predis_Commands_HashGetAll',
+
+            /* publish - subscribe */
+            'subscribe'                 => 'Predis_Commands_Subscribe',
+            'unsubscribe'               => 'Predis_Commands_Unsubscribe',
+            'psubscribe'                => 'Predis_Commands_SubscribeByPattern',
+            'punsubscribe'              => 'Predis_Commands_UnsubscribeByPattern',
+            'publish'                   => 'Predis_Commands_Publish',
+
+            /* remote server control commands */
+            'config'                    => 'Predis_Commands_Config',
+                'configuration'         => 'Predis_Commands_Config',
+        ));
+    }
+}
+
 /* ------------------------------------------------------------------------- */
 
 /* miscellaneous commands */

+ 15 - 0
phpunit.xml.dist

@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<phpunit bootstrap="test/bootstrap.php">
+    <testsuites>
+        <testsuite name="Predis Test Suite">
+            <directory>test/</directory>
+        </testsuite>
+    </testsuites>
+
+    <filter>
+        <whitelist>
+            <directory suffix=".php">lib/Predis/</directory>
+        </whitelist>
+    </filter>
+</phpunit>

+ 103 - 76
test/PredisClientFeatures.php → test/ClientFeaturesTest.php

@@ -1,19 +1,14 @@
 <?php
-define('I_AM_AWARE_OF_THE_DESTRUCTIVE_POWER_OF_THIS_TEST_SUITE', false);
 
-require_once 'PHPUnit/Framework.php';
-require_once 'PredisShared.php';
-require_once '../lib/Predis_Compatibility.php';
-
-class PredisClientFeaturesTestSuite extends PHPUnit_Framework_TestCase {
+class ClientFeaturesTestSuite extends PHPUnit_Framework_TestCase {
     public $redis;
 
-    protected function setUp() { 
+    protected function setUp() {
         $this->redis = RC::getConnection();
-        $this->redis->flushDatabase();
+        $this->redis->flushdb();
     }
 
-    protected function tearDown() { 
+    protected function tearDown() {
     }
 
     protected function onNotSuccessfulTest(Exception $exception) {
@@ -89,7 +84,7 @@ class PredisClientFeaturesTestSuite extends PHPUnit_Framework_TestCase {
     function testCommand_InlineWithNoArguments() {
         $cmd = new Predis_Compatibility_v1_0_Commands_Ping();
 
-        $this->assertType('Predis_InlineCommand', $cmd);
+        $this->assertInstanceOf('Predis_InlineCommand', $cmd);
         $this->assertEquals('PING', $cmd->getCommandId());
         $this->assertFalse($cmd->closesConnection());
         $this->assertFalse($cmd->canBeHashed());
@@ -101,7 +96,7 @@ class PredisClientFeaturesTestSuite extends PHPUnit_Framework_TestCase {
         $cmd = new Predis_Compatibility_v1_0_Commands_Get();
         $cmd->setArgumentsArray(array('key'));
 
-        $this->assertType('Predis_InlineCommand', $cmd);
+        $this->assertInstanceOf('Predis_InlineCommand', $cmd);
         $this->assertEquals('GET', $cmd->getCommandId());
         $this->assertFalse($cmd->closesConnection());
         $this->assertTrue($cmd->canBeHashed());
@@ -113,7 +108,7 @@ class PredisClientFeaturesTestSuite extends PHPUnit_Framework_TestCase {
         $cmd = new Predis_Compatibility_v1_0_Commands_Set();
         $cmd->setArgumentsArray(array('key', 'value'));
 
-        $this->assertType('Predis_BulkCommand', $cmd);
+        $this->assertInstanceOf('Predis_BulkCommand', $cmd);
         $this->assertEquals('SET', $cmd->getCommandId());
         $this->assertFalse($cmd->closesConnection());
         $this->assertTrue($cmd->canBeHashed());
@@ -125,7 +120,7 @@ class PredisClientFeaturesTestSuite extends PHPUnit_Framework_TestCase {
         $cmd = new Predis_Commands_SetMultiple();
         $cmd->setArgumentsArray(array('key1', 'value1', 'key2', 'value2'));
 
-        $this->assertType('Predis_MultiBulkCommand', $cmd);
+        $this->assertInstanceOf('Predis_MultiBulkCommand', $cmd);
         $this->assertEquals('MSET', $cmd->getCommandId());
         $this->assertFalse($cmd->closesConnection());
         $this->assertFalse($cmd->canBeHashed());
@@ -155,11 +150,11 @@ class PredisClientFeaturesTestSuite extends PHPUnit_Framework_TestCase {
     /* RedisServerProfile and derivates */
 
     function testRedisServerProfile_GetSpecificVersions() {
-        $this->assertType('Predis_RedisServer_v1_0', Predis_RedisServerProfile::get('1.0'));
-        $this->assertType('Predis_RedisServer_v1_2', Predis_RedisServerProfile::get('1.2'));
-        $this->assertType('Predis_RedisServer_v2_0', Predis_RedisServerProfile::get('2.0'));
-        $this->assertType('Predis_RedisServer_vNext', Predis_RedisServerProfile::get('dev'));
-        $this->assertType('Predis_RedisServerProfile', Predis_RedisServerProfile::get('default'));
+        $this->assertInstanceOf('Predis_RedisServer_v1_0', Predis_RedisServerProfile::get('1.0'));
+        $this->assertInstanceOf('Predis_RedisServer_v1_2', Predis_RedisServerProfile::get('1.2'));
+        $this->assertInstanceOf('Predis_RedisServer_v2_0', Predis_RedisServerProfile::get('2.0'));
+        $this->assertInstanceOf('Predis_RedisServer_vNext', Predis_RedisServerProfile::get('dev'));
+        $this->assertInstanceOf('Predis_RedisServerProfile', Predis_RedisServerProfile::get('default'));
         $this->assertEquals(Predis_RedisServerProfile::get('default'), Predis_RedisServerProfile::getDefault());
     }
 
@@ -181,12 +176,12 @@ class PredisClientFeaturesTestSuite extends PHPUnit_Framework_TestCase {
         $profile = Predis_RedisServerProfile::get('1.0');
 
         $cmdNoArgs = $profile->createCommand('info');
-        $this->assertType('Predis_Compatibility_v1_0_Commands_Info', $cmdNoArgs);
+        $this->assertInstanceOf('Predis_Compatibility_v1_0_Commands_Info', $cmdNoArgs);
         $this->assertNull($cmdNoArgs->getArgument());
 
         $args = array('key1', 'key2');
         $cmdWithArgs = $profile->createCommand('mget', $args);
-        $this->assertType('Predis_Compatibility_v1_0_Commands_GetMultiple', $cmdWithArgs);
+        $this->assertInstanceOf('Predis_Compatibility_v1_0_Commands_GetMultiple', $cmdWithArgs);
         $this->assertEquals($args[0], $cmdWithArgs->getArgument()); // TODO: why?
         $this->assertEquals($args[0], $cmdWithArgs->getArgument(0));
         $this->assertEquals($args[1], $cmdWithArgs->getArgument(1));
@@ -207,7 +202,7 @@ class PredisClientFeaturesTestSuite extends PHPUnit_Framework_TestCase {
         $this->assertFalse($profile->supportsCommand($cmdId));
         $profile->registerCommand(new $cmdClass(), $cmdId);
         $this->assertTrue($profile->supportsCommand($cmdId));
-        $this->assertType($cmdClass, $profile->createCommand($cmdId));
+        $this->assertInstanceOf($cmdClass, $profile->createCommand($cmdId));
     }
 
 
@@ -279,7 +274,7 @@ class PredisClientFeaturesTestSuite extends PHPUnit_Framework_TestCase {
         catch (Predis_CommunicationException $exception) {
             $thrownException = $exception;
         }
-        $this->assertType('Predis_CommunicationException', $thrownException);
+        $this->assertInstanceOf('Predis_CommunicationException', $thrownException);
         $this->assertEquals($expectedMessage, $thrownException->getMessage());
     }
 
@@ -287,7 +282,7 @@ class PredisClientFeaturesTestSuite extends PHPUnit_Framework_TestCase {
         $connection = new Predis_Connection(RC::getConnectionParameters());
 
         $this->assertFalse($connection->isConnected());
-        $this->assertType('resource', $connection->getSocket());
+        $this->assertInternalType('resource', $connection->getSocket());
         $this->assertTrue($connection->isConnected());
     }
 
@@ -328,7 +323,7 @@ class PredisClientFeaturesTestSuite extends PHPUnit_Framework_TestCase {
         catch (Predis_CommunicationException $exception) {
             $thrownException = $exception;
         }
-        $this->assertType('Predis_CommunicationException', $thrownException);
+        $this->assertInstanceOf('Predis_CommunicationException', $thrownException);
         $this->assertEquals((float)(time() - $start), $timeout, '', 1);
     }
 
@@ -347,7 +342,7 @@ class PredisClientFeaturesTestSuite extends PHPUnit_Framework_TestCase {
         catch (Predis_CommunicationException $exception) {
             $thrownException = $exception;
         }
-        $this->assertType('Predis_CommunicationException', $thrownException);
+        $this->assertInstanceOf('Predis_CommunicationException', $thrownException);
         $this->assertEquals($expectedMessage, $thrownException->getMessage());
         $this->assertEquals((float)(time() - $start), $timeout, '', 1);
     }
@@ -357,19 +352,12 @@ class PredisClientFeaturesTestSuite extends PHPUnit_Framework_TestCase {
 
     function testResponseReader_OptionIterableMultiBulkReplies() {
         $connection = new Predis_Connection(RC::getConnectionParameters());
-        $responseReader = $connection->getResponseReader();
 
-        $responseReader->setHandler(
-            Predis_Protocol::PREFIX_MULTI_BULK, 
-            new Predis_ResponseMultiBulkHandler()
-        );
-        $this->assertType('array', $connection->rawCommand("KEYS *\r\n"));
+        $connection->getResponseReader()->setOption('iterable_multibulk', false);
+        $this->assertInternalType('array', $connection->rawCommand("KEYS *\r\n"));
 
-        $responseReader->setHandler(
-            Predis_Protocol::PREFIX_MULTI_BULK, 
-            new Predis_ResponseMultiBulkStreamHandler()
-        );
-        $this->assertType('Iterator', $connection->rawCommand("KEYS *\r\n"));
+        $connection->getResponseReader()->setOption('iterable_multibulk', true);
+        $this->assertInstanceOf('Iterator', $connection->rawCommand("KEYS *\r\n"));
     }
 
     function testResponseReader_OptionExceptionOnError() {
@@ -378,18 +366,12 @@ class PredisClientFeaturesTestSuite extends PHPUnit_Framework_TestCase {
         $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,  
-            new Predis_ResponseErrorSilentHandler()
-        );
+        $responseReader->setOption('throw_on_error', false);
         $errorReply = $connection->rawCommand($rawCmdUnexpected);
-        $this->assertType('Predis_ResponseError', $errorReply);
+        $this->assertInstanceOf('Predis_ResponseError', $errorReply);
         $this->assertEquals(RC::EXCEPTION_WRONG_TYPE, $errorReply->message);
 
-        $responseReader->setHandler(
-            Predis_Protocol::PREFIX_ERROR, 
-            new Predis_ResponseErrorHandler()
-        );
+        $responseReader->setOption('throw_on_error', true);
         $thrownException = null;
         try {
             $connection->rawCommand($rawCmdUnexpected);
@@ -397,7 +379,7 @@ class PredisClientFeaturesTestSuite extends PHPUnit_Framework_TestCase {
         catch (Predis_ServerException $exception) {
             $thrownException = $exception;
         }
-        $this->assertType('Predis_ServerException', $thrownException);
+        $this->assertInstanceOf('Predis_ServerException', $thrownException);
         $this->assertEquals(RC::EXCEPTION_WRONG_TYPE, $thrownException->getMessage());
     }
 
@@ -416,18 +398,18 @@ class PredisClientFeaturesTestSuite extends PHPUnit_Framework_TestCase {
 
         $pipe = $client->pipeline();
 
-        $this->assertType('Predis_CommandPipeline', $pipe);
-        $this->assertType('Predis_CommandPipeline', $pipe->set('foo', 'bar'));
-        $this->assertType('Predis_CommandPipeline', $pipe->set('hoge', 'piyo'));
-        $this->assertType('Predis_CommandPipeline', $pipe->mset(array(
+        $this->assertInstanceOf('Predis_CommandPipeline', $pipe);
+        $this->assertInstanceOf('Predis_CommandPipeline', $pipe->set('foo', 'bar'));
+        $this->assertInstanceOf('Predis_CommandPipeline', $pipe->set('hoge', 'piyo'));
+        $this->assertInstanceOf('Predis_CommandPipeline', $pipe->mset(array(
             'foofoo' => 'barbar', 'hogehoge' => 'piyopiyo'
         )));
-        $this->assertType('Predis_CommandPipeline', $pipe->mget(array(
+        $this->assertInstanceOf('Predis_CommandPipeline', $pipe->mget(array(
             'foo', 'hoge', 'foofoo', 'hogehoge'
         )));
 
         $replies = $pipe->execute();
-        $this->assertType('array', $replies);
+        $this->assertInternalType('array', $replies);
         $this->assertEquals(4, count($replies));
         $this->assertEquals(4, count($replies[3]));
         $this->assertEquals('barbar', $replies[3][2]);
@@ -438,7 +420,7 @@ class PredisClientFeaturesTestSuite extends PHPUnit_Framework_TestCase {
         $client->flushdb();
 
         $replies = $client->pipeline()->ping()->set('foo', 'bar')->get('foo')->execute();
-        $this->assertType('array', $replies);
+        $this->assertInternalType('array', $replies);
         $this->assertEquals('bar', $replies[2]);
     }
 
@@ -452,7 +434,7 @@ class PredisClientFeaturesTestSuite extends PHPUnit_Framework_TestCase {
             \$pipe->get('foo');
         "));
 
-        $this->assertType('array', $replies);
+        $this->assertInternalType('array', $replies);
         $this->assertEquals('bar', $replies[2]);
     }
 
@@ -472,7 +454,7 @@ class PredisClientFeaturesTestSuite extends PHPUnit_Framework_TestCase {
         catch (Predis_ClientException $exception) {
             $thrownException = $exception;
         }
-        $this->assertType('Predis_ClientException', $thrownException);
+        $this->assertInstanceOf('Predis_ClientException', $thrownException);
         $this->assertEquals($expectedMessage, $thrownException->getMessage());
 
         $this->assertFalse($client->exists('foo'));
@@ -481,7 +463,7 @@ class PredisClientFeaturesTestSuite extends PHPUnit_Framework_TestCase {
     function testCommandPipeline_ServerExceptionInCallableBlock() {
         $client = RC::getConnection();
         $client->flushdb();
-        $client->getResponseReader()->setHandler('-', new Predis_ResponseErrorSilentHandler());
+        $client->getResponseReader()->setOption('throw_on_error', false);
 
         $replies = $client->pipeline(p_anon("\$pipe", "
             \$pipe->set('foo', 'bar');
@@ -489,8 +471,8 @@ class PredisClientFeaturesTestSuite extends PHPUnit_Framework_TestCase {
             \$pipe->set('hoge', 'piyo');
         "));
 
-        $this->assertType('array', $replies);
-        $this->assertType('Predis_ResponseError', $replies[1]);
+        $this->assertInternalType('array', $replies);
+        $this->assertInstanceOf('Predis_ResponseError', $replies[1]);
         $this->assertTrue($client->exists('foo'));
         $this->assertTrue($client->exists('hoge'));
     }
@@ -505,7 +487,7 @@ class PredisClientFeaturesTestSuite extends PHPUnit_Framework_TestCase {
         $pipe->ping()->mget(array('foo', 'hoge'));
         $replies = $pipe->execute();
 
-        $this->assertType('array', $replies);
+        $this->assertInternalType('array', $replies);
         $this->assertEquals(4, count($replies));
         $this->assertEquals('bar', $replies[3][0]);
         $this->assertEquals('piyo', $replies[3][1]);
@@ -520,18 +502,18 @@ class PredisClientFeaturesTestSuite extends PHPUnit_Framework_TestCase {
 
         $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(
+        $this->assertInstanceOf('Predis_MultiExecBlock', $multi);
+        $this->assertInstanceOf('Predis_MultiExecBlock', $multi->set('foo', 'bar'));
+        $this->assertInstanceOf('Predis_MultiExecBlock', $multi->set('hoge', 'piyo'));
+        $this->assertInstanceOf('Predis_MultiExecBlock', $multi->mset(array(
             'foofoo' => 'barbar', 'hogehoge' => 'piyopiyo'
         )));
-        $this->assertType('Predis_MultiExecBlock', $multi->mget(array(
+        $this->assertInstanceOf('Predis_MultiExecBlock', $multi->mget(array(
             'foo', 'hoge', 'foofoo', 'hogehoge'
         )));
 
         $replies = $multi->execute();
-        $this->assertType('array', $replies);
+        $this->assertInternalType('array', $replies);
         $this->assertEquals(4, count($replies));
         $this->assertEquals(4, count($replies[3]));
         $this->assertEquals('barbar', $replies[3][2]);
@@ -542,7 +524,7 @@ class PredisClientFeaturesTestSuite extends PHPUnit_Framework_TestCase {
         $client->flushdb();
 
         $replies = $client->multiExec()->ping()->set('foo', 'bar')->get('foo')->execute();
-        $this->assertType('array', $replies);
+        $this->assertInternalType('array', $replies);
         $this->assertEquals('bar', $replies[2]);
     }
 
@@ -556,7 +538,7 @@ class PredisClientFeaturesTestSuite extends PHPUnit_Framework_TestCase {
             \$multi->get('foo');
         "));
 
-        $this->assertType('array', $replies);
+        $this->assertInternalType('array', $replies);
         $this->assertEquals('bar', $replies[2]);
     }
 
@@ -602,7 +584,7 @@ class PredisClientFeaturesTestSuite extends PHPUnit_Framework_TestCase {
         catch (Predis_ClientException $exception) {
             $thrownException = $exception;
         }
-        $this->assertType('Predis_ClientException', $thrownException);
+        $this->assertInstanceOf('Predis_ClientException', $thrownException);
         $this->assertEquals($expectedMessage, $thrownException->getMessage());
 
         $this->assertFalse($client->exists('foo'));
@@ -611,7 +593,7 @@ class PredisClientFeaturesTestSuite extends PHPUnit_Framework_TestCase {
     function testMultiExecBlock_ServerExceptionInCallableBlock() {
         $client = RC::getConnection();
         $client->flushdb();
-        $client->getResponseReader()->setHandler('-', new Predis_ResponseErrorSilentHandler());
+        $client->getResponseReader()->setOption('throw_on_error', false);
 
         $multi = $client->multiExec();
         $multi->set('foo', 'bar');
@@ -619,8 +601,8 @@ class PredisClientFeaturesTestSuite extends PHPUnit_Framework_TestCase {
         $multi->set('hoge', 'piyo');
         $replies = $multi->execute();
 
-        $this->assertType('array', $replies);
-        $this->assertType('Predis_ResponseError', $replies[1]);
+        $this->assertInternalType('array', $replies);
+        $this->assertInstanceOf('Predis_ResponseError', $replies[1]);
         $this->assertTrue($client->exists('foo'));
         $this->assertTrue($client->exists('hoge'));
     }
@@ -664,7 +646,7 @@ class PredisClientFeaturesTestSuite extends PHPUnit_Framework_TestCase {
         catch (PredisException $exception) {
             $thrownException = $exception;
         }
-        $this->assertType('Predis_AbortedMultiExec', $thrownException);
+        $this->assertInstanceOf('Predis_AbortedMultiExec', $thrownException);
         $this->assertEquals('The current transaction has been aborted by the server', $thrownException->getMessage());
 
         $this->assertEquals('client2', $client1->get('sentinel'));
@@ -683,7 +665,7 @@ class PredisClientFeaturesTestSuite extends PHPUnit_Framework_TestCase {
             \$tx->set('foobar', \$foo);
             \$tx->mget('foo', 'foobar');
         "));
-        $this->assertType('array', $replies);
+        $this->assertInternalType('array', $replies);
         $this->assertEquals(array(true, array('bar', 'bar')), $replies);
 
         $tx = $client->multiExec($options);
@@ -693,7 +675,7 @@ class PredisClientFeaturesTestSuite extends PHPUnit_Framework_TestCase {
                       ->set('foobar', $foo)
                       ->mget('foo', 'foobar')
                       ->execute();
-        $this->assertType('array', $replies);
+        $this->assertInternalType('array', $replies);
         $this->assertEquals(array(true, array('bar', 'bar')), $replies);
     }
 
@@ -716,7 +698,7 @@ class PredisClientFeaturesTestSuite extends PHPUnit_Framework_TestCase {
         catch (Predis_AbortedMultiExec $exception) {
             $thrownException = $exception;
         }
-        $this->assertType('Predis_AbortedMultiExec', $thrownException);
+        $this->assertInstanceOf('Predis_AbortedMultiExec', $thrownException);
         $this->assertEquals('The current transaction has been aborted by the server', $thrownException->getMessage());
         $this->assertEquals('client2', $client1->get('sentinel'));
         $this->assertEquals($retry + 1, $client1->get('attempts'));
@@ -741,10 +723,55 @@ class PredisClientFeaturesTestSuite extends PHPUnit_Framework_TestCase {
         catch (Predis_AbortedMultiExec $exception) {
             $thrownException = $exception;
         }
-        $this->assertType('Predis_AbortedMultiExec', $thrownException);
+        $this->assertInstanceOf('Predis_AbortedMultiExec', $thrownException);
         $this->assertEquals('The current transaction has been aborted by the server', $thrownException->getMessage());
         $this->assertEquals('client2', $client1->get('sentinel'));
         $this->assertEquals($retry + 1, $client1->get('attempts'));
     }
+
+    /**
+     * @expectedException InvalidArgumentException
+     */
+    function testMultiExecBlock_RetryNotAvailableWithoutBlock() {
+        $options = array('watch' => 'foo', 'retry' => 1);
+        $tx = RC::getConnection()->multiExec($options);
+        $tx->multi()->get('foo')->exec();
+    }
+
+    function testMultiExecBlock_CheckAndSet_Discard() {
+        $client = RC::getConnection();
+        $client->flushdb();
+
+        $client->set('foo', 'bar');
+        $options = array('watch' => 'foo', 'cas' => true);
+        $replies = $client->multiExec($options, p_anon("\$tx", "
+            \$tx->watch('foobar');
+            \$foo = \$tx->get('foo');
+            \$tx->multi();
+            \$tx->set('foobar', \$foo);
+            \$tx->discard();
+            \$tx->mget('foo', 'foobar');
+        "));
+        $this->assertInternalType('array', $replies);
+        $this->assertEquals(array(array('bar', null)), $replies);
+
+        $hijack = true;
+        $client->set('foo', 'bar');
+        $options = array('watch' => 'foo', 'cas' => true, 'retry' => 1);
+        $replies = $client->multiExec($options, p_anon("\$tx", "
+            \$client2 = RC::getConnection(true);
+            \$hijack = \$client2->get('foo') !== 'hijacked';
+            \$foo = \$tx->get('foo');
+            \$tx->multi();
+            \$tx->set('foobar', \$foo);
+            \$tx->discard();
+            if (\$hijack) {
+                \$client2->set('foo', 'hijacked!');
+            }
+            \$tx->mget('foo', 'foobar');
+        "));
+        $this->assertInternalType('array', $replies);
+        $this->assertEquals(array(array('hijacked!', null)), $replies);
+    }
 }
 ?>

+ 22 - 10
test/PredisShared.php

@@ -1,12 +1,23 @@
 <?php
-require_once '../lib/Predis.php';
+// -------------------------------------------------------------------------- //
+
+define('I_AM_AWARE_OF_THE_DESTRUCTIVE_POWER_OF_THIS_TEST_SUITE', false);
+
+// -------------------------------------------------------------------------- //
+
+Predis_RedisServerProfile::registerProfile('Predis_RedisServer_v1_2', '1.2');
+Predis_RedisServerProfile::registerProfile('Predis_RedisServer_v2_0', '2.0');
+Predis_RedisServerProfile::registerProfile('Predis_RedisServer_v2_2', '2.2');
 
 if (I_AM_AWARE_OF_THE_DESTRUCTIVE_POWER_OF_THIS_TEST_SUITE !== true) {
-    exit('Please set the I_AM_AWARE_OF_THE_DESTRUCTIVE_POWER_OF_THIS_TEST_SUITE constant to TRUE if you want to proceed.');
+    exit(
+        "Please set the I_AM_AWARE_OF_THE_DESTRUCTIVE_POWER_OF_THIS_TEST_SUITE " .
+        "constant to TRUE in PredisShared.php if you want to proceed.\n"
+    );
 }
 
 if (!function_exists('array_union')) {
-    function array_union(Array $a, Array $b) { 
+    function array_union(Array $a, Array $b) {
         return array_merge($a, array_diff($b, $a));
     }
 }
@@ -35,16 +46,16 @@ class RC {
 
     private static $_connection;
 
-    public static function getConnectionArguments() { 
+    public static function getConnectionArguments() {
         return array('host' => RC::SERVER_HOST, 'port' => RC::SERVER_PORT);
     }
 
-    public static function getConnectionParameters() { 
+    public static function getConnectionParameters() {
         return new Predis_ConnectionParameters(array('host' => RC::SERVER_HOST, 'port' => RC::SERVER_PORT));
     }
 
     private static function createConnection() {
-        $serverProfile = Predis_RedisServerProfile::get('dev');
+        $serverProfile = Predis_RedisServerProfile::get('2.2');
         $connection = new Predis_Client(RC::getConnectionArguments(), $serverProfile);
         $connection->connect();
         $connection->select(RC::DEFAULT_DATABASE);
@@ -73,8 +84,9 @@ class RC {
         //       in a separate process to properly test BLPOP/BRPOP
         $redisUri = sprintf('redis://%s:%d/?database=%d', RC::SERVER_HOST, RC::SERVER_PORT, RC::DEFAULT_DATABASE);
         $handle = popen('php', 'w');
+        $dir = __DIR__;
         fwrite($handle, "<?php
-        require '../lib/Predis.php';
+        require '{$dir}/../lib/Predis.php';
         \$redis = Predis_Client::create('$redisUri');
         \$redis->rpush('{$op}1', 'a');
         \$redis->rpush('{$op}2', 'b');
@@ -125,7 +137,7 @@ class RC {
         catch (Predis_ServerException $exception) {
             $thrownException = $exception;
         }
-        $testcaseInstance->assertType('Predis_ServerException', $thrownException);
+        $testcaseInstance->assertInstanceOf('Predis_ServerException', $thrownException);
         if (isset($expectedMessage)) {
             $testcaseInstance->assertEquals($expectedMessage, $thrownException->getMessage());
         }
@@ -139,7 +151,7 @@ class RC {
         catch (Predis_ClientException $exception) {
             $thrownException = $exception;
         }
-        $testcaseInstance->assertType('Predis_ClientException', $thrownException);
+        $testcaseInstance->assertInstanceOf('Predis_ClientException', $thrownException);
         if (isset($expectedMessage)) {
             $testcaseInstance->assertEquals($expectedMessage, $thrownException->getMessage());
         }
@@ -153,7 +165,7 @@ class RC {
         catch (Predis_CommunicationException $exception) {
             $thrownException = $exception;
         }
-        $testcaseInstance->assertType('Predis_CommunicationException', $thrownException);
+        $testcaseInstance->assertInstanceOf('Predis_CommunicationException', $thrownException);
         if (isset($expectedMessage)) {
             $testcaseInstance->assertEquals($expectedMessage, $thrownException->getMessage());
         }

+ 48 - 12
test/RedisCommandsTest.php

@@ -1,10 +1,6 @@
 <?php
-define('I_AM_AWARE_OF_THE_DESTRUCTIVE_POWER_OF_THIS_TEST_SUITE', false);
 
-require_once 'PHPUnit/Framework.php';
-require_once 'PredisShared.php';
-
-class RedisCommandTestSuite extends PHPUnit_Framework_TestCase {
+class RedisCommandsTestSuite extends PHPUnit_Framework_TestCase {
     public $redis;
 
     // TODO: instead of an boolean assertion against the return value 
@@ -51,9 +47,9 @@ class RedisCommandTestSuite extends PHPUnit_Framework_TestCase {
         //       respective Predis_Command::parseResponse methods. If you need that 
         //       kind of behaviour, you should use an instance of Predis_MultiExecBlock.
         $this->assertTrue($this->redis->multi());
-        $this->assertType('Predis_ResponseQueued', $this->redis->ping());
-        $this->assertType('Predis_ResponseQueued', $this->redis->echo('hello'));
-        $this->assertType('Predis_ResponseQueued', $this->redis->echo('redis'));
+        $this->assertInstanceOf('Predis_ResponseQueued', $this->redis->ping());
+        $this->assertInstanceOf('Predis_ResponseQueued', $this->redis->echo('hello'));
+        $this->assertInstanceOf('Predis_ResponseQueued', $this->redis->echo('redis'));
         $this->assertEquals(array('PONG', 'hello', 'redis'), $this->redis->exec());
 
         $this->assertTrue($this->redis->multi());
@@ -67,8 +63,8 @@ class RedisCommandTestSuite extends PHPUnit_Framework_TestCase {
 
     function testDiscard() {
         $this->assertTrue($this->redis->multi());
-        $this->assertType('Predis_ResponseQueued', $this->redis->set('foo', 'bar'));
-        $this->assertType('Predis_ResponseQueued', $this->redis->set('hoge', 'piyo'));
+        $this->assertInstanceOf('Predis_ResponseQueued', $this->redis->set('foo', 'bar'));
+        $this->assertInstanceOf('Predis_ResponseQueued', $this->redis->set('hoge', 'piyo'));
         $this->assertEquals(true, $this->redis->discard());
 
         // should throw an exception when trying to EXEC after a DISCARD
@@ -255,7 +251,7 @@ class RedisCommandTestSuite extends PHPUnit_Framework_TestCase {
         $this->assertEquals('bar', $this->redis->substr('var', 3, 5));
         $this->assertEquals('bar', $this->redis->substr('var', -3, -1));
 
-        $this->assertNull($this->redis->substr('var', 5, 0));
+        $this->assertEquals($this->redis->substr('var', 5, 0), '');
 
         $this->redis->set('numeric', 123456789);
         $this->assertEquals(12345, $this->redis->substr('numeric', 0, 4));
@@ -1004,6 +1000,9 @@ class RedisCommandTestSuite extends PHPUnit_Framework_TestCase {
         $this->redis->set('foo', 'bar');
         $this->assertEquals(count($setA), $this->redis->sinterstore('foo', 'setA'));
 
+        // accepts an array for the list of source keys
+        $this->assertEquals(4, $this->redis->sinterstore('setC', array('setA', 'setB')));
+
         // wrong type
         $this->redis->set('foo', 'bar');
         RC::testForServerException($this, RC::EXCEPTION_WRONG_TYPE, p_anon("\$test", "
@@ -1067,6 +1066,9 @@ class RedisCommandTestSuite extends PHPUnit_Framework_TestCase {
         $this->redis->set('foo', 'bar');
         $this->assertEquals(count($setA), $this->redis->sunionstore('foo', 'setA'));
 
+        // accepts an array for the list of source keys
+        $this->assertEquals(9, $this->redis->sunionstore('setC', array('setA', 'setB')));
+
         // wrong type
         $this->redis->set('foo', 'bar');
         RC::testForServerException($this, RC::EXCEPTION_WRONG_TYPE, p_anon("\$test", "
@@ -1495,6 +1497,23 @@ class RedisCommandTestSuite extends PHPUnit_Framework_TestCase {
             $this->redis->zrange('zsetc', 0, -1, 'withscores')
         );
 
+        // using an array to pass the list of source keys
+        $sourceKeys = array('zseta', 'zsetb');
+
+        $this->assertEquals(4, $this->redis->zunionstore('zsetc', $sourceKeys));
+        $this->assertEquals(
+            array(array('a', 1), array('b', 3), array('d', 3), array('c', 5)),
+            $this->redis->zrange('zsetc', 0, -1, 'withscores')
+        );
+
+        // using an array to pass the list of source keys + options array
+        $options = array('weights' => array(2, 3));
+        $this->assertEquals(4, $this->redis->zunionstore('zsetc', $sourceKeys, $options));
+        $this->assertEquals(
+            array(array('a', 2), array('b', 7), array('d', 9), array('c', 12)),
+            $this->redis->zrange('zsetc', 0, -1, 'withscores')
+        );
+
         RC::testForServerException($this, RC::EXCEPTION_WRONG_TYPE, p_anon("\$test", "
             \$test->redis->set('zsetFake', 'fake');
             \$test->redis->zunionstore('zsetc', 2, 'zseta', 'zsetFake');
@@ -1540,6 +1559,23 @@ class RedisCommandTestSuite extends PHPUnit_Framework_TestCase {
             $this->redis->zrange('zsetc', 0, -1, 'withscores')
         );
 
+        // using an array to pass the list of source keys
+        $sourceKeys = array('zseta', 'zsetb');
+
+        $this->assertEquals(2, $this->redis->zinterstore('zsetc', $sourceKeys));
+        $this->assertEquals(
+            array(array('b', 3), array('c', 5)),
+            $this->redis->zrange('zsetc', 0, -1, 'withscores')
+        );
+
+        // using an array to pass the list of source keys + options array
+        $options = array('weights' => array(2, 3));
+        $this->assertEquals(2, $this->redis->zinterstore('zsetc', $sourceKeys, $options));
+        $this->assertEquals(
+            array(array('b', 7), array('c', 12)),
+            $this->redis->zrange('zsetc', 0, -1, 'withscores')
+        );
+
         RC::testForServerException($this, RC::EXCEPTION_WRONG_TYPE, p_anon("\$test", "
             \$test->redis->set('zsetFake', 'fake');
             \$test->redis->zinterstore('zsetc', 2, 'zseta', 'zsetFake');
@@ -1989,7 +2025,7 @@ class RedisCommandTestSuite extends PHPUnit_Framework_TestCase {
     function testInfo() {
         $serverInfo = $this->redis->info();
 
-        $this->assertType('array', $serverInfo);
+        $this->assertInternalType('array', $serverInfo);
         $this->assertNotNull($serverInfo['redis_version']);
         $this->assertGreaterThan(0, $serverInfo['uptime_in_seconds']);
         $this->assertGreaterThan(0, $serverInfo['total_connections_received']);

+ 9 - 0
test/bootstrap.php

@@ -0,0 +1,9 @@
+<?php
+
+if (!defined(__DIR__)) {
+    define('__DIR__', dirname(__FILE__));
+}
+
+require_once(__DIR__ . '/../lib/Predis.php');
+require_once(__DIR__ . '/../lib/Predis_Compatibility.php');
+require_once(__DIR__ . '/../test/PredisShared.php');

部分文件因文件數量過多而無法顯示