smsonayla.org - c99shell

!C99Shell v.2.1 [PHP 7 Update] [1.12.2019]!

Software: LiteSpeed. PHP/7.4.33 

uname -a: Linux server704.web-hosting.com 4.18.0-553.54.1.lve.el8.x86_64 #1 SMP Wed Jun 4 13:01:13
UTC 2025 x86_64
 

uid=1309(necipbey) gid=1314(necipbey) groups=1314(necipbey) 

Safe-mode: OFF (not secure)

/opt/alt/tests/alt-php80-pecl-redis_5.3.7-5.el8/tests/   drwxr-xr-x
Free 39.15 GB of 48.91 GB (80.03%)
Home    Back    Forward    UPDIR    Refresh    Search    Buffer    Encoder    Tools    Proc.    FTP brute    Sec.    SQL    PHP-code    Update    Feedback    Self remove    Logout    


Viewing file:     RedisTest.php (272.24 KB)      -rw-r--r--
Select action/file-type:
(+) | (+) | (+) | Code (+) | Session (+) | (+) | SDB (+) | (+) | (+) | (+) | (+) | (+) |
<?php defined('PHPREDIS_TESTRUN') or die("Use TestRedis.php to run tests!\n");

require_once(
dirname($_SERVER['PHP_SELF'])."/TestSuite.php");

class 
Redis_Test extends TestSuite
{
    
/* City lat/long */
    
protected $cities = [
        
'Chico'         => [-121.83747839.728494],
        
'Sacramento'    => [-121.49440038.581572],
        
'Gridley'       => [-121.69358339.363777],
        
'Marysville'    => [-121.59135539.145725],
        
'Cupertino'     => [-122.03218237.322998]
    ];

    protected 
$serializers = [
        
Redis::SERIALIZER_NONE,
        
Redis::SERIALIZER_PHP,
    ];

    
/**
     * @var Redis
     */
    
public $redis;

    
/**
     * @var string
     */
    
protected $sessionPrefix 'PHPREDIS_SESSION:';

    
/**
     * @var string
     */
    
protected $sessionSaveHandler 'redis';

    public function 
setUp() {
        
$this->redis $this->newInstance();
        
$info $this->redis->info();
        
$this->version = (isset($info['redis_version'])?$info['redis_version']:'0.0.0');

        if (
defined('Redis::SERIALIZER_IGBINARY')) {
            
$this->serializers[] = Redis::SERIALIZER_IGBINARY;
        }
    }

    protected function 
minVersionCheck($version) {
        return 
version_compare($this->version$version) >= 0;
    }

    protected function 
mstime() {
        return 
round(microtime(true)*1000);
    }

    protected function 
getAuthParts(&$user, &$pass) {
        
$user $pass NULL;

        
$auth $this->getAuth();
        if ( ! 
$auth)
            return;

        if (
is_array($auth)) {
            if (
count($auth) > 1) {
                list(
$user$pass) = $auth;
            } else {
                
$pass $auth[0];
            }
        } else {
            
$pass $auth;
        }
    }

    protected function 
getAuthFragment() {
        static 
$_authidx 0;
        
$_authidx++;

        
$this->getAuthParts($user$pass);

        if (
$user && $pass) {
            if (
$_authidx == 0)
                return 
"auth[user]=$user&auth[pass]=$pass";
            else
                return 
"auth[]=$user&auth[]=$pass";
        } else if (
$pass) {
            if (
$_authidx == 0)
                return 
"auth[pass]=$pass";
            if (
$_authidx == 0)
                return 
"auth[]=$pass";
            else
                return 
"auth=$pass";
        } else {
            return 
NULL;
        }
    }

    protected function 
getFullHostPath()
    {
        
$fullHostPath parent::getFullHostPath();
        
$authFragment $this->getAuthFragment();

        if (isset(
$fullHostPath) && $authFragment) {
            
$fullHostPath .= "?$authFragment";
        }
        return 
$fullHostPath;
    }

    protected function 
newInstance() {
        
$r = new Redis();

        
$r->connect($this->getHost(), $this->getPort());

        if(
$this->getAuth()) {
            
$this->assertTrue($r->auth($this->getAuth()));
        }
        return 
$r;
    }

    public function 
tearDown() {
        if(
$this->redis) {
            
$this->redis->close();
        }
    }

    public function 
reset()
    {
        
$this->setUp();
        
$this->tearDown();
    }

    
/* Helper function to determine if the clsas has pipeline support */
    
protected function havePipeline() {
        
$str_constant get_class($this->redis) . '::PIPELINE';
        return 
defined($str_constant);
    }

    public function 
testMinimumVersion()
    {
        
// Minimum server version required for tests
        
$this->assertTrue(version_compare($this->version"2.4.0") >= 0);
    }

    public function 
testPing() {
        
/* Reply literal off */
        
$this->assertTrue($this->redis->ping());
        
$this->assertTrue($this->redis->ping(NULL));
        
$this->assertEquals('BEEP'$this->redis->ping('BEEP'));

        
/* Make sure we're good in MULTI mode */
        
$this->redis->multi();

        
$this->redis->ping();
        
$this->redis->ping('BEEP');
        
$this->assertEquals([true'BEEP'], $this->redis->exec());
    }

    public function 
testPipelinePublish() {
        if (!
$this->havePipeline()) {
            
$this->markTestSkipped();
        }

        
$ret $this->redis->pipeline()
            ->
publish('chan''msg')
            ->
exec();

        
$this->assertTrue(is_array($ret) && count($ret) === && $ret[0] >= 0);
    }

    
// Run some simple tests against the PUBSUB command.  This is problematic, as we
    // can't be sure what's going on in the instance, but we can do some things.
    
public function testPubSub() {
        
// Only available since 2.8.0
        
if (version_compare($this->version"2.8.0") < 0) {
            
$this->markTestSkipped();
            return;
        }

        
// PUBSUB CHANNELS ...
        
$result $this->redis->pubsub("channels""*");
        
$this->assertTrue(is_array($result));
        
$result $this->redis->pubsub("channels");
        
$this->assertTrue(is_array($result));

        
// PUBSUB NUMSUB

        
$c1 uniqid() . '-' rand(1,100);
        
$c2 uniqid() . '-' rand(1,100);

        
$result $this->redis->pubsub("numsub", [$c1$c2]);

        
// Should get an array back, with two elements
        
$this->assertTrue(is_array($result));
        
$this->assertEquals(count($result), 2);

        
// Make sure the elements are correct, and have zero counts
        
foreach([$c1,$c2] as $channel) {
            
$this->assertTrue(isset($result[$channel]));
            
$this->assertEquals($result[$channel], 0);
        }

        
// PUBSUB NUMPAT
        
$result $this->redis->pubsub("numpat");
        
$this->assertTrue(is_int($result));

        
// Invalid calls
        
$this->assertFalse($this->redis->pubsub("notacommand"));
        
$this->assertFalse($this->redis->pubsub("numsub""not-an-array"));
    }

    public function 
testBitsets() {

        
$this->redis->del('key');
        
$this->assertEquals(0$this->redis->getBit('key'0));
        
$this->assertEquals(FALSE$this->redis->getBit('key', -1));
        
$this->assertEquals(0$this->redis->getBit('key'100000));

        
$this->redis->set('key'"\xff");
        for(
$i 0$i 8$i++) {
            
$this->assertEquals(1$this->redis->getBit('key'$i));
        }
        
$this->assertEquals(0$this->redis->getBit('key'8));

        
// change bit 0
        
$this->assertEquals(1$this->redis->setBit('key'00));
        
$this->assertEquals(0$this->redis->setBit('key'00));
        
$this->assertEquals(0$this->redis->getBit('key'0));
        
$this->assertEquals("\x7f"$this->redis->get('key'));

        
// change bit 1
        
$this->assertEquals(1$this->redis->setBit('key'10));
        
$this->assertEquals(0$this->redis->setBit('key'10));
        
$this->assertEquals(0$this->redis->getBit('key'1));
        
$this->assertEquals("\x3f"$this->redis->get('key'));

        
// change bit > 1
        
$this->assertEquals(1$this->redis->setBit('key'20));
        
$this->assertEquals(0$this->redis->setBit('key'20));
        
$this->assertEquals(0$this->redis->getBit('key'2));
        
$this->assertEquals("\x1f"$this->redis->get('key'));

        
// values above 1 are changed to 1 but don't overflow on bits to the right.
        
$this->assertEquals(0$this->redis->setBit('key'00xff));
        
$this->assertEquals("\x9f"$this->redis->get('key'));

        
// Verify valid offset ranges
        
$this->assertFalse($this->redis->getBit('key', -1));

        
$this->redis->setBit('key'0x7fffffff1);
        
$this->assertEquals(1$this->redis->getBit('key'0x7fffffff));
    }

    public function 
testBitPos() {
        if (
version_compare($this->version"2.8.7") < 0) {
            
$this->MarkTestSkipped();
            return;
        }

        
$this->redis->del('bpkey');

        
$this->redis->set('bpkey'"\xff\xf0\x00");
        
$this->assertEquals($this->redis->bitpos('bpkey'0), 12);

        
$this->redis->set('bpkey'"\x00\xff\xf0");
        
$this->assertEquals($this->redis->bitpos('bpkey'10), 8);
        
$this->assertEquals($this->redis->bitpos('bpkey'11), 8);

        
$this->redis->set('bpkey'"\x00\x00\x00");
        
$this->assertEquals($this->redis->bitpos('bpkey'1), -1);
    }

    public function 
test1000() {

     
$s str_repeat('A'1000);
     
$this->redis->set('x'$s);
     
$this->assertEquals($s$this->redis->get('x'));

     
$s str_repeat('A'1000000);
     
$this->redis->set('x'$s);
     
$this->assertEquals($s$this->redis->get('x'));
    }

    public function 
testEcho() {
        
$this->assertEquals($this->redis->echo("hello"), "hello");
        
$this->assertEquals($this->redis->echo(""), "");
        
$this->assertEquals($this->redis->echo(" 0123 "), " 0123 ");
    }

    public function 
testErr() {

     
$this->redis->set('x''-ERR');
     
$this->assertEquals($this->redis->get('x'), '-ERR');

    }

    public function 
testSet()
    {
        
$this->assertEquals(TRUE$this->redis->set('key''nil'));
        
$this->assertEquals('nil'$this->redis->get('key'));

        
$this->assertEquals(TRUE$this->redis->set('key''val'));

        
$this->assertEquals('val'$this->redis->get('key'));
        
$this->assertEquals('val'$this->redis->get('key'));
        
$this->redis->del('keyNotExist');
        
$this->assertEquals(FALSE$this->redis->get('keyNotExist'));

        
$this->redis->set('key2''val');
        
$this->assertEquals('val'$this->redis->get('key2'));

        
$value 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA';

        
$this->redis->set('key2'$value);
        
$this->assertEquals($value$this->redis->get('key2'));
        
$this->assertEquals($value$this->redis->get('key2'));

        
$this->redis->del('key');
        
$this->redis->del('key2');


        
$i 66000;
        
$value2 'X';
        while(
$i--) {
            
$value2 .= 'A';
        }
        
$value2 .= 'X';

        
$this->redis->set('key'$value2);
        
$this->assertEquals($value2$this->redis->get('key'));
        
$this->redis->del('key');
        
$this->assertEquals(False$this->redis->get('key'));

        
$data gzcompress('42');
        
$this->assertEquals(True$this->redis->set('key'$data));
        
$this->assertEquals('42'gzuncompress($this->redis->get('key')));

        
$this->redis->del('key');
        
$data gzcompress('value1');
        
$this->assertEquals(True$this->redis->set('key'$data));
        
$this->assertEquals('value1'gzuncompress($this->redis->get('key')));

        
$this->redis->del('key');
        
$this->assertEquals(TRUE$this->redis->set('key'0));
        
$this->assertEquals('0'$this->redis->get('key'));
        
$this->assertEquals(TRUE$this->redis->set('key'1));
        
$this->assertEquals('1'$this->redis->get('key'));
        
$this->assertEquals(TRUE$this->redis->set('key'0.1));
        
$this->assertEquals('0.1'$this->redis->get('key'));
        
$this->assertEquals(TRUE$this->redis->set('key''0.1'));
        
$this->assertEquals('0.1'$this->redis->get('key'));
        
$this->assertEquals(TRUE$this->redis->set('key'TRUE));
        
$this->assertEquals('1'$this->redis->get('key'));

        
$this->assertEquals(True$this->redis->set('key'''));
        
$this->assertEquals(''$this->redis->get('key'));
        
$this->assertEquals(True$this->redis->set('key'NULL));
        
$this->assertEquals(''$this->redis->get('key'));

        
$this->assertEquals(True$this->redis->set('key'gzcompress('42')));
        
$this->assertEquals('42'gzuncompress($this->redis->get('key')));
    }

    
/* Extended SET options for Redis >= 2.6.12 */
    
public function testExtendedSet() {
        
// Skip the test if we don't have a new enough version of Redis
        
if (version_compare($this->version'2.6.12') < 0) {
            
$this->markTestSkipped();
            return;
        }

        
/* Legacy SETEX redirection */
        
$this->redis->del('foo');
        
$this->assertTrue($this->redis->set('foo','bar'20));
        
$this->assertEquals($this->redis->get('foo'), 'bar');
        
$this->assertEquals($this->redis->ttl('foo'), 20);

        
/* Should coerce doubles into long */
        
$this->assertTrue($this->redis->set('foo''bar-20.5'20.5));
        
$this->assertEquals($this->redis->ttl('foo'), 20);
        
$this->assertEquals($this->redis->get('foo'), 'bar-20.5');

        
/* Invalid third arguments */
        
$this->assertFalse(@$this->redis->set('foo','bar','baz'));
        
$this->assertFalse(@$this->redis->set('foo','bar',new StdClass()));

        
/* Set if not exist */
        
$this->redis->del('foo');
        
$this->assertTrue($this->redis->set('foo','bar', ['nx']));
        
$this->assertEquals($this->redis->get('foo'), 'bar');
        
$this->assertFalse($this->redis->set('foo','bar', ['nx']));

        
/* Set if exists */
        
$this->assertTrue($this->redis->set('foo','bar', ['xx']));
        
$this->assertEquals($this->redis->get('foo'), 'bar');
        
$this->redis->del('foo');
        
$this->assertFalse($this->redis->set('foo','bar', ['xx']));

        
/* Set with a TTL */
        
$this->assertTrue($this->redis->set('foo','bar', ['ex'=>100]));
        
$this->assertEquals($this->redis->ttl('foo'), 100);

        
/* Set with a PTTL */
        
$this->assertTrue($this->redis->set('foo','bar',['px'=>100000]));
        
$this->assertTrue(100000 $this->redis->pttl('foo') < 1000);

        
/* Set if exists, with a TTL */
        
$this->assertTrue($this->redis->set('foo','bar',['xx','ex'=>105]));
        
$this->assertEquals($this->redis->ttl('foo'), 105);
        
$this->assertEquals($this->redis->get('foo'), 'bar');

        
/* Set if not exists, with a TTL */
        
$this->redis->del('foo');
        
$this->assertTrue($this->redis->set('foo','bar', ['nx''ex'=>110]));
        
$this->assertEquals($this->redis->ttl('foo'), 110);
        
$this->assertEquals($this->redis->get('foo'), 'bar');
        
$this->assertFalse($this->redis->set('foo','bar', ['nx''ex'=>110]));

        
/* Throw some nonsense into the array, and check that the TTL came through */
        
$this->redis->del('foo');
        
$this->assertTrue($this->redis->set('foo','barbaz', ['not-valid','nx','invalid','ex'=>200]));
        
$this->assertEquals($this->redis->ttl('foo'), 200);
        
$this->assertEquals($this->redis->get('foo'), 'barbaz');

        
/* Pass NULL as the optional arguments which should be ignored */
        
$this->redis->del('foo');
        
$this->redis->set('foo','bar'NULL);
        
$this->assertEquals($this->redis->get('foo'), 'bar');
        
$this->assertTrue($this->redis->ttl('foo')<0);

        
/* Make sure we ignore bad/non-string options (regression test for #1835) */
        
$this->assertTrue($this->redis->set('foo''bar', [NULL'EX' => 60]));
        
$this->assertTrue($this->redis->set('foo''bar', [NULL, new stdClass(), 'EX' => 60]));
        
$this->assertFalse(@$this->redis->set('foo''bar', [NULL'EX' => []]));

        if (
version_compare($this->version"6.0.0") < 0)
            return;

        
/* KEEPTTL works by itself */
        
$this->redis->set('foo''bar', ['EX' => 100]);
        
$this->redis->set('foo''bar', ['KEEPTTL']);
        
$this->assertTrue($this->redis->ttl('foo') > -1);

        
/* Works with other options */
        
$this->redis->set('foo''bar', ['XX''KEEPTTL']);
        
$this->assertTrue($this->redis->ttl('foo') > -1);
        
$this->redis->set('foo''bar', ['XX']);
        
$this->assertTrue($this->redis->ttl('foo') == -1);
    }

    public function 
testGetSet() {
        
$this->redis->del('key');
        
$this->assertTrue($this->redis->getSet('key''42') === FALSE);
        
$this->assertTrue($this->redis->getSet('key''123') === '42');
        
$this->assertTrue($this->redis->getSet('key''123') === '123');
    }

    public function 
testRandomKey() {
        for(
$i 0$i 1000$i++) {
            
$k $this->redis->randomKey();
            
$this->assertEquals($this->redis->exists($k), 1);
        }
    }

    public function 
testRename() {
        
// strings
        
$this->redis->del('{key}0');
        
$this->redis->set('{key}0''val0');
        
$this->redis->rename('{key}0''{key}1');
        
$this->assertEquals(FALSE$this->redis->get('{key}0'));
        
$this->assertEquals('val0'$this->redis->get('{key}1'));
    }

    public function 
testRenameNx() {
        
// strings
        
$this->redis->del('{key}0''{key}1');
        
$this->redis->set('{key}0''val0');
        
$this->redis->set('{key}1''val1');
        
$this->assertTrue($this->redis->renameNx('{key}0''{key}1') === FALSE);
        
$this->assertTrue($this->redis->get('{key}0') === 'val0');
        
$this->assertTrue($this->redis->get('{key}1') === 'val1');

        
// lists
        
$this->redis->del('{key}0');
        
$this->redis->del('{key}1');
        
$this->redis->lPush('{key}0''val0');
        
$this->redis->lPush('{key}0''val1');
        
$this->redis->lPush('{key}1''val1-0');
        
$this->redis->lPush('{key}1''val1-1');
        
$this->assertTrue($this->redis->renameNx('{key}0''{key}1') === FALSE);
        
$this->assertTrue($this->redis->lRange('{key}0'0, -1) === ['val1''val0']);
        
$this->assertTrue($this->redis->lRange('{key}1'0, -1) === ['val1-1''val1-0']);

        
$this->redis->del('{key}2');
        
$this->assertTrue($this->redis->renameNx('{key}0''{key}2') === TRUE);
        
$this->assertTrue($this->redis->lRange('{key}0'0, -1) === []);
        
$this->assertTrue($this->redis->lRange('{key}2'0, -1) === ['val1''val0']);
    }

    public function 
testMultiple() {
        
$this->redis->del('k1');
        
$this->redis->del('k2');
        
$this->redis->del('k3');

        
$this->redis->set('k1''v1');
        
$this->redis->set('k2''v2');
        
$this->redis->set('k3''v3');
        
$this->redis->set(1'test');

        
$this->assertEquals(['v1'], $this->redis->mget(['k1']));
        
$this->assertEquals(['v1''v3'false], $this->redis->mget(['k1''k3''NoKey']));
        
$this->assertEquals(['v1''v2''v3'], $this->redis->mget(['k1''k2''k3']));
        
$this->assertEquals(['v1''v2''v3'], $this->redis->mget(['k1''k2''k3']));

        
$this->redis->set('k5''$1111111111');
        
$this->assertEquals([=> '$1111111111'], $this->redis->mget(['k5']));

        
$this->assertEquals([=> 'test'], $this->redis->mget([1])); // non-string
    
}

    public function 
testMultipleBin() {

    
$this->redis->del('k1');
        
$this->redis->del('k2');
        
$this->redis->del('k3');

        
$this->redis->set('k1'gzcompress('v1'));
        
$this->redis->set('k2'gzcompress('v2'));
        
$this->redis->set('k3'gzcompress('v3'));

        
$this->assertEquals([gzcompress('v1'), gzcompress('v2'), gzcompress('v3')], $this->redis->mget(['k1''k2''k3']));
        
$this->assertEquals([gzcompress('v1'), gzcompress('v2'), gzcompress('v3')], $this->redis->mget(['k1''k2''k3']));

    }

    public function 
testSetTimeout() {
        
$this->redis->del('key');
        
$this->redis->set('key''value');
        
$this->assertEquals('value'$this->redis->get('key'));
        
$this->redis->expire('key'1);
        
$this->assertEquals('value'$this->redis->get('key'));
        
sleep(2);
        
$this->assertEquals(False$this->redis->get('key'));
    }

    
/* This test is prone to failure in the Travis container, so attempt to mitigate this by running more than once */
    
public function testExpireAt() {
        
$success false;

        for (
$i 0; !$success && $i 3$i++) {
            
$this->redis->del('key');
            
$this->redis->set('key''value');
            
$this->redis->expireAt('key'time() + 1);
            
usleep(1500000);
            
$success FALSE === $this->redis->get('key');
        }

        
$this->assertTrue($success);
    }

    public function 
testSetEx() {

        
$this->redis->del('key');
        
$this->assertTrue($this->redis->setex('key'7'val') === TRUE);
        
$this->assertTrue($this->redis->ttl('key') ===7);
        
$this->assertTrue($this->redis->get('key') === 'val');
    }

    public function 
testPSetEx() {
        
$this->redis->del('key');
        
$this->assertTrue($this->redis->psetex('key'1000'val') === TRUE);
        
$this->assertTrue($this->redis->ttl('key') ===7);
        
$this->assertTrue($this->redis->get('key') === 'val');
    }

    public function 
testSetNX() {

        
$this->redis->set('key'42);
        
$this->assertTrue($this->redis->setnx('key''err') === FALSE);
        
$this->assertTrue($this->redis->get('key') === '42');

        
$this->redis->del('key');
        
$this->assertTrue($this->redis->setnx('key''42') === TRUE);
        
$this->assertTrue($this->redis->get('key') === '42');
    }

    public function 
testExpireAtWithLong() {
        if (
PHP_INT_SIZE != 8) {
            
$this->markTestSkipped('64 bits only');
        }
        
$longExpiryTimeExceedingInt 3153600000;
        
$this->redis->del('key');
        
$this->assertTrue($this->redis->setex('key'$longExpiryTimeExceedingInt'val') === TRUE);
        
$this->assertTrue($this->redis->ttl('key') === $longExpiryTimeExceedingInt);
    }

    public function 
testIncr()
    {
        
$this->redis->set('key'0);

        
$this->redis->incr('key');
        
$this->assertEquals(1, (int)$this->redis->get('key'));

        
$this->redis->incr('key');
        
$this->assertEquals(2, (int)$this->redis->get('key'));

        
$this->redis->incrBy('key'3);
        
$this->assertEquals(5, (int)$this->redis->get('key'));

        
$this->redis->incrBy('key'1);
        
$this->assertEquals(6, (int)$this->redis->get('key'));

        
$this->redis->incrBy('key', -1);
        
$this->assertEquals(5, (int)$this->redis->get('key'));

        
$this->redis->incr('key'5);
        
$this->assertEquals(10, (int)$this->redis->get('key'));

        
$this->redis->del('key');

        
$this->redis->set('key''abc');

        
$this->redis->incr('key');
        
$this->assertTrue("abc" === $this->redis->get('key'));

        
$this->redis->incr('key');
        
$this->assertTrue("abc" === $this->redis->get('key'));

        
$this->redis->set('key'0);
        
$this->assertEquals(PHP_INT_MAX$this->redis->incrby('key'PHP_INT_MAX));
    }

    public function 
testIncrByFloat()
    {
        
// incrbyfloat is new in 2.6.0
        
if (version_compare($this->version"2.5.0") < 0) {
            
$this->markTestSkipped();
        }

        
$this->redis->del('key');

        
$this->redis->set('key'0);

        
$this->redis->incrbyfloat('key'1.5);
        
$this->assertEquals('1.5'$this->redis->get('key'));

        
$this->redis->incrbyfloat('key'2.25);
        
$this->assertEquals('3.75'$this->redis->get('key'));

        
$this->redis->incrbyfloat('key', -2.25);
        
$this->assertEquals('1.5'$this->redis->get('key'));

        
$this->redis->set('key''abc');

        
$this->redis->incrbyfloat('key'1.5);
        
$this->assertTrue("abc" === $this->redis->get('key'));

        
$this->redis->incrbyfloat('key', -1.5);
        
$this->assertTrue("abc" === $this->redis->get('key'));

        
// Test with prefixing
        
$this->redis->setOption(Redis::OPT_PREFIX'someprefix:');
        
$this->redis->del('key');
        
$this->redis->incrbyfloat('key',1.8);
        
$this->assertEquals(1.8floatval($this->redis->get('key'))); // convert to float to avoid rounding issue on arm
        
$this->redis->setOption(Redis::OPT_PREFIX'');
        
$this->assertEquals(1$this->redis->exists('someprefix:key'));
        
$this->redis->del('someprefix:key');

    }

    public function 
testDecr()
    {
        
$this->redis->set('key'5);

        
$this->redis->decr('key');
        
$this->assertEquals(4, (int)$this->redis->get('key'));

        
$this->redis->decr('key');
        
$this->assertEquals(3, (int)$this->redis->get('key'));

        
$this->redis->decrBy('key'2);
        
$this->assertEquals(1, (int)$this->redis->get('key'));

        
$this->redis->decrBy('key'1);
        
$this->assertEquals(0, (int)$this->redis->get('key'));

        
$this->redis->decrBy('key', -10);
        
$this->assertEquals(10, (int)$this->redis->get('key'));

        
$this->redis->decr('key'10);
        
$this->assertEquals(0, (int)$this->redis->get('key'));
    }


    public function 
testExists()
    {
        
/* Single key */
        
$this->redis->del('key');
        
$this->assertEquals(0$this->redis->exists('key'));
        
$this->redis->set('key''val');
        
$this->assertEquals(1$this->redis->exists('key'));

        
/* Add multiple keys */
        
$mkeys = [];
        for (
$i 0$i 10$i++) {
            if (
rand(12) == 1) {
                
$mkey "{exists}key:$i";
                
$this->redis->set($mkey$i);
                
$mkeys[] = $mkey;
            }
        }

        
/* Test passing an array as well as the keys variadic */
        
$this->assertEquals(count($mkeys), $this->redis->exists($mkeys));
        
$this->assertEquals(count($mkeys), call_user_func_array([$this->redis'exists'], $mkeys));
    }

    public function 
testKeys()
    {
        
$pattern 'keys-test-';
        for(
$i 1$i 10$i++) {
            
$this->redis->set($pattern.$i$i);
        }
        
$this->redis->del($pattern.'3');
        
$keys $this->redis->keys($pattern.'*');

        
$this->redis->set($pattern.'3''something');

        
$keys2 $this->redis->keys($pattern.'*');

        
$this->assertEquals((count($keys) + 1), count($keys2));

        
// empty array when no key matches
        
$this->assertEquals([], $this->redis->keys(rand().rand().rand().'*'));
    }

    protected function 
genericDelUnlink($cmd) {
        
$key 'key' rand();
        
$this->redis->set($key'val');
        
$this->assertEquals('val'$this->redis->get($key));
        
$this->assertEquals(1$this->redis->$cmd($key));
        
$this->assertEquals(false$this->redis->get($key));

        
// multiple, all existing
        
$this->redis->set('x'0);
        
$this->redis->set('y'1);
        
$this->redis->set('z'2);
        
$this->assertEquals(3$this->redis->$cmd('x''y''z'));
        
$this->assertEquals(false$this->redis->get('x'));
        
$this->assertEquals(false$this->redis->get('y'));
        
$this->assertEquals(false$this->redis->get('z'));

        
// multiple, none existing
        
$this->assertEquals(0$this->redis->$cmd('x''y''z'));
        
$this->assertEquals(false$this->redis->get('x'));
        
$this->assertEquals(false$this->redis->get('y'));
        
$this->assertEquals(false$this->redis->get('z'));

        
// multiple, some existing
        
$this->redis->set('y'1);
        
$this->assertEquals(1$this->redis->$cmd('x''y''z'));
        
$this->assertEquals(false$this->redis->get('y'));

        
$this->redis->set('x'0);
        
$this->redis->set('y'1);
        
$this->assertEquals(2$this->redis->$cmd(['x''y']));
    }

    public function 
testDelete() {
        
$this->genericDelUnlink("DEL");
    }

    public function 
testUnlink() {
        if (
version_compare($this->version"4.0.0") < 0) {
            
$this->markTestSkipped();
            return;
        }

        
$this->genericDelUnlink("UNLINK");
    }

    public function 
testType()
    {
        
// 0 => none, (key didn't exist)
        // 1=> string,
        // 2 => set,
        // 3 => list,
        // 4 => zset,
        // 5 => hash
        // 6 => stream

        // string
        
$this->redis->set('key''val');
        
$this->assertEquals(Redis::REDIS_STRING$this->redis->type('key'));

        
// list
        
$this->redis->lPush('keyList''val0');
        
$this->redis->lPush('keyList''val1');
        
$this->assertEquals(Redis::REDIS_LIST$this->redis->type('keyList'));

        
// set
        
$this->redis->del('keySet');
        
$this->redis->sAdd('keySet''val0');
        
$this->redis->sAdd('keySet''val1');
        
$this->assertEquals(Redis::REDIS_SET$this->redis->type('keySet'));

        
// sadd with numeric key
        
$this->redis->del(123);
        
$this->assertTrue(=== $this->redis->sAdd(123'val0'));
        
$this->assertTrue(['val0'] === $this->redis->sMembers(123));

        
// zset
        
$this->redis->del('keyZSet');
        
$this->redis->zAdd('keyZSet'0'val0');
        
$this->redis->zAdd('keyZSet'1'val1');
        
$this->assertEquals(Redis::REDIS_ZSET$this->redis->type('keyZSet'));

        
// hash
        
$this->redis->del('keyHash');
        
$this->redis->hSet('keyHash''key0''val0');
        
$this->redis->hSet('keyHash''key1''val1');
        
$this->assertEquals(Redis::REDIS_HASH$this->redis->type('keyHash'));

        
// stream
        
if ($this->minVersionCheck("5.0")) {
            
$this->redis->del('stream');
            
$this->redis->xAdd('stream''*', ['foo' => 'bar']);
            
$this->assertEquals(Redis::REDIS_STREAM$this->redis->type('stream'));
        }

        
// None
        
$this->redis->del('keyNotExists');
        
$this->assertEquals(Redis::REDIS_NOT_FOUND$this->redis->type('keyNotExists'));

    }

    public function 
testStr() {

        
$this->redis->set('key''val1');
        
$this->assertTrue($this->redis->append('key''val2') === 8);
        
$this->assertTrue($this->redis->get('key') === 'val1val2');

        
$this->redis->del('keyNotExist');
        
$this->assertTrue($this->redis->append('keyNotExist''value') === 5);
        
$this->assertTrue($this->redis->get('keyNotExist') === 'value');

        
$this->redis->set('key''This is a string') ;
        
$this->assertTrue($this->redis->getRange('key'03) === 'This');
        
$this->assertTrue($this->redis->getRange('key', -6, -1) === 'string');
        
$this->assertTrue($this->redis->getRange('key', -6100000) === 'string');
        
$this->assertTrue($this->redis->get('key') === 'This is a string');

        
$this->redis->set('key''This is a string') ;
        
$this->assertTrue($this->redis->strlen('key') === 16);

        
$this->redis->set('key'10) ;
        
$this->assertTrue($this->redis->strlen('key') === 2);
        
$this->redis->set('key''') ;
        
$this->assertTrue($this->redis->strlen('key') === 0);
        
$this->redis->set('key''000') ;
        
$this->assertTrue($this->redis->strlen('key') === 3);
    }

    
// PUSH, POP : LPUSH, LPOP
    
public function testlPop()
    {

    
//  rpush  => tail
    //  lpush => head


        
$this->redis->del('list');

        
$this->redis->lPush('list''val');
        
$this->redis->lPush('list''val2');
    
$this->redis->rPush('list''val3');

    
// 'list' = [ 'val2', 'val', 'val3']

    
$this->assertEquals('val2'$this->redis->lPop('list'));
        
$this->assertEquals('val'$this->redis->lPop('list'));
        
$this->assertEquals('val3'$this->redis->lPop('list'));
        
$this->assertEquals(FALSE$this->redis->lPop('list'));

    
// testing binary data

    
$this->redis->del('list');
    
$this->assertEquals(1$this->redis->lPush('list'gzcompress('val1')));
    
$this->assertEquals(2$this->redis->lPush('list'gzcompress('val2')));
    
$this->assertEquals(3$this->redis->lPush('list'gzcompress('val3')));

    
$this->assertEquals('val3'gzuncompress($this->redis->lPop('list')));
    
$this->assertEquals('val2'gzuncompress($this->redis->lPop('list')));
    
$this->assertEquals('val1'gzuncompress($this->redis->lPop('list')));

    }

    
// PUSH, POP : RPUSH, RPOP
    
public function testrPop()
    {
    
//  rpush  => tail
    //  lpush => head

        
$this->redis->del('list');

        
$this->redis->rPush('list''val');
        
$this->redis->rPush('list''val2');
    
$this->redis->lPush('list''val3');

    
// 'list' = [ 'val3', 'val', 'val2']

    
$this->assertEquals('val2'$this->redis->rPop('list'));
        
$this->assertEquals('val'$this->redis->rPop('list'));
        
$this->assertEquals('val3'$this->redis->rPop('list'));
        
$this->assertEquals(FALSE$this->redis->rPop('list'));

    
// testing binary data

    
$this->redis->del('list');
    
$this->assertEquals(1$this->redis->rPush('list'gzcompress('val1')));
    
$this->assertEquals(2$this->redis->rPush('list'gzcompress('val2')));
    
$this->assertEquals(3$this->redis->rPush('list'gzcompress('val3')));

    
$this->assertEquals('val3'gzuncompress($this->redis->rPop('list')));
    
$this->assertEquals('val2'gzuncompress($this->redis->rPop('list')));
    
$this->assertEquals('val1'gzuncompress($this->redis->rPop('list')));

    }

    public function 
testblockingPop() {
        
// non blocking blPop, brPop
        
$this->redis->del('list');
        
$this->redis->lPush('list''val1');
        
$this->redis->lPush('list''val2');
        
$this->assertTrue($this->redis->blPop(['list'], 2) === ['list''val2']);
        
$this->assertTrue($this->redis->blPop(['list'], 2) === ['list''val1']);

        
$this->redis->del('list');
        
$this->redis->lPush('list''val1');
        
$this->redis->lPush('list''val2');
        
$this->assertTrue($this->redis->brPop(['list'], 1) === ['list''val1']);
        
$this->assertTrue($this->redis->brPop(['list'], 1) === ['list''val2']);

        
// blocking blpop, brpop
        
$this->redis->del('list');

        
/* Also test our option that we want *-1 to be returned as NULL */
        
foreach ([false => [], true => NULL] as $opt => $val) {
            
$this->redis->setOption(Redis::OPT_NULL_MULTIBULK_AS_NULL$opt);
            
$this->assertEquals($val$this->redis->blPop(['list'], 1));
            
$this->assertEquals($val$this->redis->brPop(['list'], 1));
        }

        
$this->redis->setOption(Redis::OPT_NULL_MULTIBULK_AS_NULLfalse);
    }

    public function 
testllen()
    {
        
$this->redis->del('list');

        
$this->redis->lPush('list''val');
        
$this->assertEquals(1$this->redis->llen('list'));

        
$this->redis->lPush('list''val2');
        
$this->assertEquals(2$this->redis->llen('list'));

        
$this->assertEquals('val2'$this->redis->lPop('list'));
        
$this->assertEquals(1$this->redis->llen('list'));

        
$this->assertEquals('val'$this->redis->lPop('list'));
        
$this->assertEquals(0$this->redis->llen('list'));

        
$this->assertEquals(FALSE$this->redis->lPop('list'));
        
$this->assertEquals(0$this->redis->llen('list'));    // empty returns 0

        
$this->redis->del('list');
        
$this->assertEquals(0$this->redis->llen('list'));    // non-existent returns 0

        
$this->redis->set('list''actually not a list');
        
$this->assertEquals(FALSE$this->redis->llen('list'));// not a list returns FALSE
    
}

    
//lInsert, lPopx, rPopx
    
public function testlPopx() {
        
//test lPushx/rPushx
        
$this->redis->del('keyNotExists');
        
$this->assertTrue($this->redis->lPushx('keyNotExists''value') === 0);
        
$this->assertTrue($this->redis->rPushx('keyNotExists''value') === 0);

        
$this->redis->del('key');
        
$this->redis->lPush('key''val0');
        
$this->assertTrue($this->redis->lPushx('key''val1') === 2);
        
$this->assertTrue($this->redis->rPushx('key''val2') === 3);
        
$this->assertTrue($this->redis->lrange('key'0, -1) === ['val1''val0''val2']);

        
//test linsert
        
$this->redis->del('key');
        
$this->redis->lPush('key''val0');
        
$this->assertTrue($this->redis->lInsert('keyNotExists'Redis::AFTER'val1''val2') === 0);
        
$this->assertTrue($this->redis->lInsert('key'Redis::BEFORE'valX''val2') === -1);

        
$this->assertTrue($this->redis->lInsert('key'Redis::AFTER'val0''val1') === 2);
        
$this->assertTrue($this->redis->lInsert('key'Redis::BEFORE'val0''val2') === 3);
        
$this->assertTrue($this->redis->lrange('key'0, -1) === ['val2''val0''val1']);
    }

    
// ltrim, lsize, lpop
    
public function testltrim()
    {

        
$this->redis->del('list');

        
$this->redis->lPush('list''val');
        
$this->redis->lPush('list''val2');
        
$this->redis->lPush('list''val3');
        
$this->redis->lPush('list''val4');

    
$this->assertEquals(TRUE$this->redis->ltrim('list'02));
    
$this->assertEquals(3$this->redis->llen('list'));

        
$this->redis->ltrim('list'00);
        
$this->assertEquals(1$this->redis->llen('list'));
    
$this->assertEquals('val4'$this->redis->lPop('list'));

    
$this->assertEquals(TRUE$this->redis->ltrim('list'1010000));
    
$this->assertEquals(TRUE$this->redis->ltrim('list'1000010));

    
// test invalid type
    
$this->redis->set('list''not a list...');
    
$this->assertEquals(FALSE$this->redis->ltrim('list'02));

    }

    public function 
setupSort() {
        
// people with name, age, salary
        
$this->redis->set('person:name_1''Alice');
        
$this->redis->set('person:age_1'27);
        
$this->redis->set('person:salary_1'2500);

        
$this->redis->set('person:name_2''Bob');
        
$this->redis->set('person:age_2'34);
        
$this->redis->set('person:salary_2'2000);

        
$this->redis->set('person:name_3''Carol');
        
$this->redis->set('person:age_3'25);
        
$this->redis->set('person:salary_3'2800);

        
$this->redis->set('person:name_4''Dave');
        
$this->redis->set('person:age_4'41);
        
$this->redis->set('person:salary_4'3100);

        
// set-up
        
$this->redis->del('person:id');
        foreach([
1,2,3,4] as $id) {
            
$this->redis->lPush('person:id'$id);
        }
    }

    public function 
testSortPrefix() {
        
// Make sure that sorting works with a prefix
        
$this->redis->setOption(Redis::OPT_PREFIX'some-prefix:');
        
$this->redis->del('some-item');
        
$this->redis->sadd('some-item'1);
        
$this->redis->sadd('some-item'2);
        
$this->redis->sadd('some-item'3);

        
$this->assertEquals(['1','2','3'], $this->redis->sort('some-item', ['sort' => 'asc']));
        
$this->assertEquals(['3','2','1'], $this->redis->sort('some-item', ['sort' => 'desc']));
        
$this->assertEquals(['1','2','3'], $this->redis->sort('some-item'));

        
// Kill our set/prefix
        
$this->redis->del('some-item');
        
$this->redis->setOption(Redis::OPT_PREFIX'');
    }

    public function 
testSortAsc() {
        
$this->setupSort();
        
// sort by age and get IDs
        
$byAgeAsc = ['3','1','2','4'];
        
$this->assertEquals($byAgeAsc$this->redis->sort('person:id', ['by' => 'person:age_*']));
        
$this->assertEquals($byAgeAsc$this->redis->sort('person:id', ['by' => 'person:age_*''sort' => 'asc']));
        
$this->assertEquals(['1''2''3''4'], $this->redis->sort('person:id', ['by' => NULL]));   // check that NULL works.
        
$this->assertEquals(['1''2''3''4'], $this->redis->sort('person:id', ['by' => NULL'get' => NULL])); // for all fields.
        
$this->assertEquals(['1''2''3''4'], $this->redis->sort('person:id', ['sort' => 'asc']));

        
// sort by age and get names
        
$byAgeAsc = ['Carol','Alice','Bob','Dave'];
        
$this->assertEquals($byAgeAsc$this->redis->sort('person:id', ['by' => 'person:age_*''get' => 'person:name_*']));
        
$this->assertEquals($byAgeAsc$this->redis->sort('person:id', ['by' => 'person:age_*''get' => 'person:name_*''sort' => 'asc']));

        
$this->assertEquals(array_slice($byAgeAsc02), $this->redis->sort('person:id', ['by' => 'person:age_*''get' => 'person:name_*''limit' => [02]]));
        
$this->assertEquals(array_slice($byAgeAsc02), $this->redis->sort('person:id', ['by' => 'person:age_*''get' => 'person:name_*''limit' => [02], 'sort' => 'asc']));

        
$this->assertEquals(array_slice($byAgeAsc12), $this->redis->sort('person:id', ['by' => 'person:age_*''get' => 'person:name_*''limit' => [12]]));
        
$this->assertEquals(array_slice($byAgeAsc12), $this->redis->sort('person:id', ['by' => 'person:age_*''get' => 'person:name_*''limit' => [12], 'sort' => 'asc']));
        
$this->assertEquals($byAgeAsc$this->redis->sort('person:id', ['by' => 'person:age_*''get' => 'person:name_*''limit' => [04]]));
        
$this->assertEquals($byAgeAsc$this->redis->sort('person:id', ['by' => 'person:age_*''get' => 'person:name_*''limit' => [0"4"]])); // with strings
        
$this->assertEquals($byAgeAsc$this->redis->sort('person:id', ['by' => 'person:age_*''get' => 'person:name_*''limit' => ["0"4]]));

        
// sort by salary and get ages
        
$agesBySalaryAsc = ['34''27''25''41'];
        
$this->assertEquals($agesBySalaryAsc$this->redis->sort('person:id', ['by' => 'person:salary_*''get' => 'person:age_*']));
        
$this->assertEquals($agesBySalaryAsc$this->redis->sort('person:id', ['by' => 'person:salary_*''get' => 'person:age_*''sort' => 'asc']));

        
$agesAndSalaries $this->redis->sort('person:id', ['by' => 'person:salary_*''get' => ['person:age_*''person:salary_*'], 'sort' => 'asc']);
        
$this->assertEquals(['34''2000''27''2500''25''2800''41''3100'], $agesAndSalaries);

        
// sort non-alpha doesn't change all-string lists
        // list â†’ [ghi, def, abc]
        
$list = ['abc''def''ghi'];
        
$this->redis->del('list');
        foreach(
$list as $i) {
            
$this->redis->lPush('list'$i);
        }

        
// SORT list â†’ [ghi, def, abc]
        
if (version_compare($this->version"2.5.0") < 0) {
            
$this->assertEquals(array_reverse($list), $this->redis->sort('list'));
            
$this->assertEquals(array_reverse($list), $this->redis->sort('list', ['sort' => 'asc']));
        } else {
            
// TODO rewrite, from 2.6.0 release notes:
            // SORT now will refuse to sort in numerical mode elements that can't be parsed
            // as numbers
        
}

        
// SORT list ALPHA â†’ [abc, def, ghi]
        
$this->assertEquals($list$this->redis->sort('list', ['alpha' => TRUE]));
        
$this->assertEquals($list$this->redis->sort('list', ['sort' => 'asc''alpha' => TRUE]));
    }

    public function 
testSortDesc() {

    
$this->setupSort();

    
// sort by age and get IDs
    
$byAgeDesc = ['4','2','1','3'];
    
$this->assertEquals($byAgeDesc$this->redis->sort('person:id', ['by' => 'person:age_*''sort' => 'desc']));

    
// sort by age and get names
    
$byAgeDesc = ['Dave''Bob''Alice''Carol'];
    
$this->assertEquals($byAgeDesc$this->redis->sort('person:id', ['by' => 'person:age_*''get' => 'person:name_*''sort' => 'desc']));

    
$this->assertEquals(array_slice($byAgeDesc02), $this->redis->sort('person:id', ['by' => 'person:age_*''get' => 'person:name_*''limit' => [02], 'sort' => 'desc']));
    
$this->assertEquals(array_slice($byAgeDesc12), $this->redis->sort('person:id', ['by' => 'person:age_*''get' => 'person:name_*''limit' => [12], 'sort' => 'desc']));

    
// sort by salary and get ages
    
$agesBySalaryDesc = ['41''25''27''34'];
    
$this->assertEquals($agesBySalaryDesc$this->redis->sort('person:id', ['by' => 'person:salary_*''get' => 'person:age_*''sort' => 'desc']));

    
// sort non-alpha doesn't change all-string lists
    
$list = ['def''abc''ghi'];
    
$this->redis->del('list');
    foreach(
$list as $i) {
        
$this->redis->lPush('list'$i);
    }

    
// SORT list â†’ [ghi, abc, def]
    
if (version_compare($this->version"2.5.0") < 0) {
        
$this->assertEquals(array_reverse($list), $this->redis->sort('list', ['sort' => 'desc']));
    } else {
        
// TODO rewrite, from 2.6.0 release notes:
        // SORT now will refuse to sort in numerical mode elements that can't be parsed
        // as numbers
    
}

    
// SORT list ALPHA â†’ [abc, def, ghi]
    
$this->assertEquals(['ghi''def''abc'], $this->redis->sort('list', ['sort' => 'desc''alpha' => TRUE]));
    }

    
// LINDEX
    
public function testLindex() {

        
$this->redis->del('list');

        
$this->redis->lPush('list''val');
        
$this->redis->lPush('list''val2');
        
$this->redis->lPush('list''val3');

        
$this->assertEquals('val3'$this->redis->lIndex('list'0));
        
$this->assertEquals('val2'$this->redis->lIndex('list'1));
        
$this->assertEquals('val'$this->redis->lIndex('list'2));
        
$this->assertEquals('val'$this->redis->lIndex('list', -1));
        
$this->assertEquals('val2'$this->redis->lIndex('list', -2));
        
$this->assertEquals('val3'$this->redis->lIndex('list', -3));
        
$this->assertEquals(FALSE$this->redis->lIndex('list', -4));

        
$this->redis->rPush('list''val4');
        
$this->assertEquals('val4'$this->redis->lIndex('list'3));
        
$this->assertEquals('val4'$this->redis->lIndex('list', -1));
    }

    
// lRem testing
    
public function testlrem() {
        
$this->redis->del('list');
        
$this->redis->lPush('list''a');
        
$this->redis->lPush('list''b');
        
$this->redis->lPush('list''c');
        
$this->redis->lPush('list''c');
        
$this->redis->lPush('list''b');
        
$this->redis->lPush('list''c');

        
// ['c', 'b', 'c', 'c', 'b', 'a']
        
$return $this->redis->lrem('list''b'2);
        
// ['c', 'c', 'c', 'a']
        
$this->assertEquals(2$return);
        
$this->assertEquals('c'$this->redis->lIndex('list'0));
        
$this->assertEquals('c'$this->redis->lIndex('list'1));
        
$this->assertEquals('c'$this->redis->lIndex('list'2));
        
$this->assertEquals('a'$this->redis->lIndex('list'3));

        
$this->redis->del('list');
        
$this->redis->lPush('list''a');
        
$this->redis->lPush('list''b');
        
$this->redis->lPush('list''c');
        
$this->redis->lPush('list''c');
        
$this->redis->lPush('list''b');
        
$this->redis->lPush('list''c');

        
// ['c', 'b', 'c', 'c', 'b', 'a']
        
$this->redis->lrem('list''c', -2);
        
// ['c', 'b', 'b', 'a']
        
$this->assertEquals(2$return);
        
$this->assertEquals('c'$this->redis->lIndex('list'0));
        
$this->assertEquals('b'$this->redis->lIndex('list'1));
        
$this->assertEquals('b'$this->redis->lIndex('list'2));
        
$this->assertEquals('a'$this->redis->lIndex('list'3));

        
// remove each element
        
$this->assertEquals(1$this->redis->lrem('list''a'0));
        
$this->assertEquals(0$this->redis->lrem('list''x'0));
        
$this->assertEquals(2$this->redis->lrem('list''b'0));
        
$this->assertEquals(1$this->redis->lrem('list''c'0));
        
$this->assertEquals(FALSE$this->redis->get('list'));

        
$this->redis->set('list''actually not a list');
        
$this->assertEquals(FALSE$this->redis->lrem('list''x'));
    }

    public function 
testsAdd() {
        
$this->redis->del('set');

        
$this->assertEquals(1$this->redis->sAdd('set''val'));
        
$this->assertEquals(0$this->redis->sAdd('set''val'));

        
$this->assertTrue($this->redis->sismember('set''val'));
        
$this->assertFalse($this->redis->sismember('set''val2'));

        
$this->assertEquals(1$this->redis->sAdd('set''val2'));

        
$this->assertTrue($this->redis->sismember('set''val2'));
    }

    public function 
testscard() {
        
$this->redis->del('set');
        
$this->assertEquals(1$this->redis->sAdd('set''val'));
        
$this->assertEquals(1$this->redis->scard('set'));
        
$this->assertEquals(1$this->redis->sAdd('set''val2'));
        
$this->assertEquals(2$this->redis->scard('set'));
    }

    public function 
testsrem() {
        
$this->redis->del('set');
        
$this->redis->sAdd('set''val');
        
$this->redis->sAdd('set''val2');
        
$this->redis->srem('set''val');
        
$this->assertEquals(1$this->redis->scard('set'));
        
$this->redis->srem('set''val2');
        
$this->assertEquals(0$this->redis->scard('set'));
    }

    public function 
testsMove() {
        
$this->redis->del('{set}0');
        
$this->redis->del('{set}1');

        
$this->redis->sAdd('{set}0''val');
        
$this->redis->sAdd('{set}0''val2');

        
$this->assertTrue($this->redis->sMove('{set}0''{set}1''val'));
        
$this->assertFalse($this->redis->sMove('{set}0''{set}1''val'));
        
$this->assertFalse($this->redis->sMove('{set}0''{set}1''val-what'));

        
$this->assertEquals(1$this->redis->scard('{set}0'));
        
$this->assertEquals(1$this->redis->scard('{set}1'));

        
$this->assertEquals(['val2'], $this->redis->smembers('{set}0'));
        
$this->assertEquals(['val'], $this->redis->smembers('{set}1'));
    }

    public function 
testsPop() {
        
$this->redis->del('set0');
        
$this->assertTrue($this->redis->sPop('set0') === FALSE);

        
$this->redis->sAdd('set0''val');
        
$this->redis->sAdd('set0''val2');

        
$v0 $this->redis->sPop('set0');
        
$this->assertTrue(=== $this->redis->scard('set0'));
        
$this->assertTrue($v0 === 'val' || $v0 === 'val2');
        
$v1 $this->redis->sPop('set0');
        
$this->assertTrue(=== $this->redis->scard('set0'));
        
$this->assertTrue(($v0 === 'val' && $v1 === 'val2') || ($v1 === 'val' && $v0 === 'val2'));

        
$this->assertTrue($this->redis->sPop('set0') === FALSE);
    }

    public function 
testsPopWithCount() {
        if (!
$this->minVersionCheck("3.2")) {
            return 
$this->markTestSkipped();
        }

        
$set 'set0';
        
$prefix 'member';
        
$count 5;

        
/* Add a few members */
        
$this->redis->del($set);
        for (
$i 0$i $count$i++) {
            
$this->redis->sadd($set$prefix.$i);
        }

        
/* Pop them all */
        
$ret $this->redis->sPop($set$i);

        
/* Make sure we got an arary and the count is right */
        
if ($this->assertTrue(is_array($ret)) && $this->assertTrue(count($ret) == $count)) {
            
/* Probably overkill but validate the actual returned members */
            
for ($i 0$i $count$i++) {
                
$this->assertTrue(in_array($prefix.$i$ret));
            }
        }
    }

    public function 
testsRandMember() {
        
$this->redis->del('set0');
        
$this->assertTrue($this->redis->sRandMember('set0') === FALSE);

        
$this->redis->sAdd('set0''val');
        
$this->redis->sAdd('set0''val2');

        
$got = [];
        while(
true) {
            
$v $this->redis->sRandMember('set0');
            
$this->assertTrue(=== $this->redis->scard('set0')); // no change.
            
$this->assertTrue($v === 'val' || $v === 'val2');

            
$got[$v] = $v;
            if(
count($got) == 2) {
                break;
            }
        }

        
//
        // With and without count, while serializing
        //

        
$this->redis->del('set0');
        
$this->redis->setOption(Redis::OPT_SERIALIZERRedis::SERIALIZER_PHP);
        for(
$i=0;$i<5;$i++) {
            
$member "member:$i";
            
$this->redis->sAdd('set0'$member);
            
$mems[] = $member;
        }

        
$member $this->redis->srandmember('set0');
        
$this->assertTrue(in_array($member$mems));

        
$rmembers $this->redis->srandmember('set0'$i);
        foreach(
$rmembers as $reply_mem) {
            
$this->assertTrue(in_array($reply_mem$mems));
        }

        
$this->redis->setOption(Redis::OPT_SERIALIZERRedis::SERIALIZER_NONE);
    }

    public function 
testSRandMemberWithCount() {
        
// Make sure the set is nuked
        
$this->redis->del('set0');

        
// Run with a count (positive and negative) on an empty set
        
$ret_pos $this->redis->sRandMember('set0'10);
        
$ret_neg $this->redis->sRandMember('set0', -10);

        
// Should both be empty arrays
        
$this->assertTrue(is_array($ret_pos) && empty($ret_pos));
        
$this->assertTrue(is_array($ret_neg) && empty($ret_neg));

        
// Add a few items to the set
        
for($i=0;$i<100;$i++) {
            
$this->redis->sadd('set0'"member$i");
        }

        
// Get less than the size of the list
        
$ret_slice $this->redis->srandmember('set0'20);

        
// Should be an array with 20 items
        
$this->assertTrue(is_array($ret_slice) && count($ret_slice) == 20);

        
// Ask for more items than are in the list (but with a positive count)
        
$ret_slice $this->redis->srandmember('set0'200);

        
// Should be an array, should be however big the set is, exactly
        
$this->assertTrue(is_array($ret_slice) && count($ret_slice) == $i);

        
// Now ask for too many items but negative
        
$ret_slice $this->redis->srandmember('set0', -200);

        
// Should be an array, should have exactly the # of items we asked for (will be dups)
        
$this->assertTrue(is_array($ret_slice) && count($ret_slice) == 200);

        
//
        // Test in a pipeline
        //

        
if ($this->havePipeline()) {
            
$pipe $this->redis->pipeline();

            
$pipe->srandmember('set0'20);
            
$pipe->srandmember('set0'200);
            
$pipe->srandmember('set0', -200);

            
$ret $this->redis->exec();

            
$this->assertTrue(is_array($ret[0]) && count($ret[0]) == 20);
            
$this->assertTrue(is_array($ret[1]) && count($ret[1]) == $i);
            
$this->assertTrue(is_array($ret[2]) && count($ret[2]) == 200);

            
// Kill the set
            
$this->redis->del('set0');
        }
    }

    public function 
testsismember()
    {
        
$this->redis->del('set');

        
$this->redis->sAdd('set''val');

        
$this->assertTrue($this->redis->sismember('set''val'));
        
$this->assertFalse($this->redis->sismember('set''val2'));
    }

    public function 
testsmembers()
    {
        
$this->redis->del('set');

        
$this->redis->sAdd('set''val');
        
$this->redis->sAdd('set''val2');
        
$this->redis->sAdd('set''val3');

        
$array = ['val''val2''val3'];

        
$smembers $this->redis->smembers('set');
        
sort($smembers);
        
$this->assertEquals($array$smembers);

        
$sMembers $this->redis->sMembers('set');
        
sort($sMembers);
        
$this->assertEquals($array$sMembers); // test alias
    
}

    public function 
testsMisMember()
    {
        
// Only available since 6.2.0
        
if (version_compare($this->version'6.2.0') < 0) {
            
$this->markTestSkipped();
            return;
        }

        
$this->redis->del('set');

        
$this->redis->sAdd('set''val');
        
$this->redis->sAdd('set''val2');
        
$this->redis->sAdd('set''val3');

        
$misMembers $this->redis->sMisMember('set''val''notamember''val3');
        
$this->assertEquals([101], $misMembers);

        
$misMembers $this->redis->sMisMember('wrongkey''val''val2''val3');
        
$this->assertEquals([000], $misMembers);
    }

    public function 
testlSet() {

        
$this->redis->del('list');
        
$this->redis->lPush('list''val');
        
$this->redis->lPush('list''val2');
    
$this->redis->lPush('list''val3');

    
$this->assertEquals($this->redis->lIndex('list'0), 'val3');
    
$this->assertEquals($this->redis->lIndex('list'1), 'val2');
    
$this->assertEquals($this->redis->lIndex('list'2), 'val');

    
$this->assertEquals(TRUE$this->redis->lSet('list'1'valx'));

    
$this->assertEquals($this->redis->lIndex('list'0), 'val3');
    
$this->assertEquals($this->redis->lIndex('list'1), 'valx');
    
$this->assertEquals($this->redis->lIndex('list'2), 'val');

    }

    public function 
testsInter() {
        
$this->redis->del('{set}odd');    // set of odd numbers
        
$this->redis->del('{set}prime');  // set of prime numbers
        
$this->redis->del('{set}square'); // set of squares
        
$this->redis->del('{set}seq');    // set of numbers of the form n^2 - 1

        
$x = [1,3,5,7,9,11,13,15,17,19,21,23,25];
        foreach(
$x as $i) {
            
$this->redis->sAdd('{set}odd'$i);
        }

        
$y = [1,2,3,5,7,11,13,17,19,23];
        foreach(
$y as $i) {
            
$this->redis->sAdd('{set}prime'$i);
        }

        
$z = [1,4,9,16,25];
        foreach(
$z as $i) {
            
$this->redis->sAdd('{set}square'$i);
        }

        
$t = [2,5,10,17,26];
        foreach(
$t as $i) {
            
$this->redis->sAdd('{set}seq'$i);
        }

        
$xy $this->redis->sInter('{set}odd''{set}prime');   // odd prime numbers
        
foreach($xy as $i) {
            
$i = (int)$i;
            
$this->assertTrue(in_array($iarray_intersect($x$y)));
        }

        
$xy $this->redis->sInter(['{set}odd''{set}prime']);    // odd prime numbers, as array.
        
foreach($xy as $i) {
            
$i = (int)$i;
            
$this->assertTrue(in_array($iarray_intersect($x$y)));
        }

        
$yz $this->redis->sInter('{set}prime''{set}square');   // set of prime squares
        
foreach($yz as $i) {
            
$i = (int)$i;
            
$this->assertTrue(in_array($iarray_intersect($y$z)));
        }

        
$yz $this->redis->sInter(['{set}prime''{set}square']);    // set of odd squares, as array
        
foreach($yz as $i) {
        
$i = (int)$i;
            
$this->assertTrue(in_array($iarray_intersect($y$z)));
        }

        
$zt $this->redis->sInter('{set}square''{set}seq');   // prime squares
        
$this->assertTrue($zt === []);
        
$zt $this->redis->sInter(['{set}square''{set}seq']);    // prime squares, as array
        
$this->assertTrue($zt === []);

        
$xyz $this->redis->sInter('{set}odd''{set}prime''{set}square');// odd prime squares
        
$this->assertTrue($xyz === ['1']);

        
$xyz $this->redis->sInter(['{set}odd''{set}prime''{set}square']);// odd prime squares, with an array as a parameter
        
$this->assertTrue($xyz === ['1']);

        
$nil $this->redis->sInter([]);
        
$this->assertTrue($nil === FALSE);
    }

    public function 
testsInterStore() {
        
$this->redis->del('{set}x');  // set of odd numbers
        
$this->redis->del('{set}y');  // set of prime numbers
        
$this->redis->del('{set}z');  // set of squares
        
$this->redis->del('{set}t');  // set of numbers of the form n^2 - 1

        
$x = [1,3,5,7,9,11,13,15,17,19,21,23,25];
        foreach(
$x as $i) {
            
$this->redis->sAdd('{set}x'$i);
        }

        
$y = [1,2,3,5,7,11,13,17,19,23];
        foreach(
$y as $i) {
            
$this->redis->sAdd('{set}y'$i);
        }

        
$z = [1,4,9,16,25];
        foreach(
$z as $i) {
            
$this->redis->sAdd('{set}z'$i);
        }

        
$t = [2,5,10,17,26];
        foreach(
$t as $i) {
            
$this->redis->sAdd('{set}t'$i);
        }

        
/* Regression test for passing a single array */
        
$this->assertEquals($this->redis->sInterStore(['{set}k''{set}x''{set}y']), count(array_intersect($x,$y)));

        
$count $this->redis->sInterStore('{set}k''{set}x''{set}y');  // odd prime numbers
        
$this->assertEquals($count$this->redis->scard('{set}k'));
        foreach(
array_intersect($x$y) as $i) {
            
$this->assertTrue($this->redis->sismember('{set}k'$i));
        }

        
$count $this->redis->sInterStore('{set}k''{set}y''{set}z');  // set of odd squares
        
$this->assertEquals($count$this->redis->scard('{set}k'));
        foreach(
array_intersect($y$z) as $i) {
            
$this->assertTrue($this->redis->sismember('{set}k'$i));
        }

        
$count $this->redis->sInterStore('{set}k''{set}z''{set}t');  // squares of the form n^2 + 1
        
$this->assertEquals($count0);
        
$this->assertEquals($count$this->redis->scard('{set}k'));

        
$this->redis->del('{set}z');
        
$xyz $this->redis->sInterStore('{set}k''{set}x''{set}y''{set}z'); // only z missing, expect 0.
        
$this->assertTrue($xyz === 0);

        
$this->redis->del('{set}y');
        
$xyz $this->redis->sInterStore('{set}k''{set}x''{set}y''{set}z'); // y and z missing, expect 0.
        
$this->assertTrue($xyz === 0);

        
$this->redis->del('{set}x');
        
$xyz $this->redis->sInterStore('{set}k''{set}x''{set}y''{set}z'); // x y and z ALL missing, expect 0.
        
$this->assertTrue($xyz === 0);
    }

    public function 
testsUnion() {
        
$this->redis->del('{set}x');  // set of odd numbers
        
$this->redis->del('{set}y');  // set of prime numbers
        
$this->redis->del('{set}z');  // set of squares
        
$this->redis->del('{set}t');  // set of numbers of the form n^2 - 1

        
$x = [1,3,5,7,9,11,13,15,17,19,21,23,25];
        foreach(
$x as $i) {
            
$this->redis->sAdd('{set}x'$i);
        }

        
$y = [1,2,3,5,7,11,13,17,19,23];
        foreach(
$y as $i) {
            
$this->redis->sAdd('{set}y'$i);
        }

        
$z = [1,4,9,16,25];
        foreach(
$z as $i) {
            
$this->redis->sAdd('{set}z'$i);
        }

        
$t = [2,5,10,17,26];
        foreach(
$t as $i) {
            
$this->redis->sAdd('{set}t'$i);
        }

        
$xy $this->redis->sUnion('{set}x''{set}y');   // x U y
        
foreach($xy as $i) {
        
$i = (int)$i;
            
$this->assertTrue(in_array($iarray_merge($x$y)));
        }

        
$yz $this->redis->sUnion('{set}y''{set}z');   // y U Z
        
foreach($yz as $i) {
        
$i = (int)$i;
            
$this->assertTrue(in_array($iarray_merge($y$z)));
        }

        
$zt $this->redis->sUnion('{set}z''{set}t');   // z U t
        
foreach($zt as $i) {
        
$i = (int)$i;
            
$this->assertTrue(in_array($iarray_merge($z$t)));
        }

        
$xyz $this->redis->sUnion('{set}x''{set}y''{set}z'); // x U y U z
        
foreach($xyz as $i) {
        
$i = (int)$i;
            
$this->assertTrue(in_array($iarray_merge($x$y$z)));
        }
    }

    public function 
testsUnionStore() {
        
$this->redis->del('{set}x');  // set of odd numbers
        
$this->redis->del('{set}y');  // set of prime numbers
        
$this->redis->del('{set}z');  // set of squares
        
$this->redis->del('{set}t');  // set of numbers of the form n^2 - 1

        
$x = [1,3,5,7,9,11,13,15,17,19,21,23,25];
        foreach(
$x as $i) {
            
$this->redis->sAdd('{set}x'$i);
        }

        
$y = [1,2,3,5,7,11,13,17,19,23];
        foreach(
$y as $i) {
            
$this->redis->sAdd('{set}y'$i);
        }

        
$z = [1,4,9,16,25];
        foreach(
$z as $i) {
            
$this->redis->sAdd('{set}z'$i);
        }

        
$t = [2,5,10,17,26];
        foreach(
$t as $i) {
            
$this->redis->sAdd('{set}t'$i);
        }

        
$count $this->redis->sUnionStore('{set}k''{set}x''{set}y');  // x U y
        
$xy array_unique(array_merge($x$y));
        
$this->assertEquals($countcount($xy));
        foreach(
$xy as $i) {
        
$i = (int)$i;
            
$this->assertTrue($this->redis->sismember('{set}k'$i));
        }

        
$count $this->redis->sUnionStore('{set}k''{set}y''{set}z');  // y U z
        
$yz array_unique(array_merge($y$z));
        
$this->assertEquals($countcount($yz));
        foreach(
$yz as $i) {
        
$i = (int)$i;
            
$this->assertTrue($this->redis->sismember('{set}k'$i));
        }

        
$count $this->redis->sUnionStore('{set}k''{set}z''{set}t');  // z U t
        
$zt array_unique(array_merge($z$t));
        
$this->assertEquals($countcount($zt));
        foreach(
$zt as $i) {
        
$i = (int)$i;
            
$this->assertTrue($this->redis->sismember('{set}k'$i));
        }

        
$count $this->redis->sUnionStore('{set}k''{set}x''{set}y''{set}z'); // x U y U z
        
$xyz array_unique(array_merge($x$y$z));
        
$this->assertEquals($countcount($xyz));
        foreach(
$xyz as $i) {
        
$i = (int)$i;
            
$this->assertTrue($this->redis->sismember('{set}k'$i));
        }

        
$this->redis->del('{set}x');  // x missing now
        
$count $this->redis->sUnionStore('{set}k''{set}x''{set}y''{set}z'); // x U y U z
        
$this->assertTrue($count === count(array_unique(array_merge($y$z))));

        
$this->redis->del('{set}y');  // x and y missing
        
$count $this->redis->sUnionStore('{set}k''{set}x''{set}y''{set}z'); // x U y U z
        
$this->assertTrue($count === count(array_unique($z)));

        
$this->redis->del('{set}z');  // x, y, and z ALL missing
        
$count $this->redis->sUnionStore('{set}k''{set}x''{set}y''{set}z'); // x U y U z
        
$this->assertTrue($count === 0);
    }

    public function 
testsDiff() {
        
$this->redis->del('{set}x');  // set of odd numbers
        
$this->redis->del('{set}y');  // set of prime numbers
        
$this->redis->del('{set}z');  // set of squares
        
$this->redis->del('{set}t');  // set of numbers of the form n^2 - 1

        
$x = [1,3,5,7,9,11,13,15,17,19,21,23,25];
        foreach(
$x as $i) {
            
$this->redis->sAdd('{set}x'$i);
        }

        
$y = [1,2,3,5,7,11,13,17,19,23];
        foreach(
$y as $i) {
            
$this->redis->sAdd('{set}y'$i);
        }

        
$z = [1,4,9,16,25];
        foreach(
$z as $i) {
            
$this->redis->sAdd('{set}z'$i);
        }

        
$t = [2,5,10,17,26];
        foreach(
$t as $i) {
            
$this->redis->sAdd('{set}t'$i);
        }

        
$xy $this->redis->sDiff('{set}x''{set}y');    // x U y
        
foreach($xy as $i) {
        
$i = (int)$i;
            
$this->assertTrue(in_array($iarray_diff($x$y)));
        }

        
$yz $this->redis->sDiff('{set}y''{set}z');    // y U Z
        
foreach($yz as $i) {
        
$i = (int)$i;
            
$this->assertTrue(in_array($iarray_diff($y$z)));
        }

        
$zt $this->redis->sDiff('{set}z''{set}t');    // z U t
        
foreach($zt as $i) {
        
$i = (int)$i;
            
$this->assertTrue(in_array($iarray_diff($z$t)));
        }

        
$xyz $this->redis->sDiff('{set}x''{set}y''{set}z'); // x U y U z
        
foreach($xyz as $i) {
        
$i = (int)$i;
            
$this->assertTrue(in_array($iarray_diff($x$y$z)));
        }
    }

    public function 
testsDiffStore() {
        
$this->redis->del('{set}x');  // set of odd numbers
        
$this->redis->del('{set}y');  // set of prime numbers
        
$this->redis->del('{set}z');  // set of squares
        
$this->redis->del('{set}t');  // set of numbers of the form n^2 - 1

        
$x = [1,3,5,7,9,11,13,15,17,19,21,23,25];
        foreach(
$x as $i) {
            
$this->redis->sAdd('{set}x'$i);
        }

        
$y = [1,2,3,5,7,11,13,17,19,23];
        foreach(
$y as $i) {
            
$this->redis->sAdd('{set}y'$i);
        }

        
$z = [1,4,9,16,25];
        foreach(
$z as $i) {
            
$this->redis->sAdd('{set}z'$i);
        }

        
$t = [2,5,10,17,26];
        foreach(
$t as $i) {
            
$this->redis->sAdd('{set}t'$i);
        }

        
$count $this->redis->sDiffStore('{set}k''{set}x''{set}y');   // x - y
        
$xy array_unique(array_diff($x$y));
        
$this->assertEquals($countcount($xy));
        foreach(
$xy as $i) {
            
$i = (int)$i;
            
$this->assertTrue($this->redis->sismember('{set}k'$i));
        }

        
$count $this->redis->sDiffStore('{set}k''{set}y''{set}z');   // y - z
        
$yz array_unique(array_diff($y$z));
        
$this->assertEquals($countcount($yz));
        foreach(
$yz as $i) {
        
$i = (int)$i;
            
$this->assertTrue($this->redis->sismember('{set}k'$i));
        }

        
$count $this->redis->sDiffStore('{set}k''{set}z''{set}t');   // z - t
        
$zt array_unique(array_diff($z$t));
        
$this->assertEquals($countcount($zt));
        foreach(
$zt as $i) {
        
$i = (int)$i;
            
$this->assertTrue($this->redis->sismember('{set}k'$i));
        }

        
$count $this->redis->sDiffStore('{set}k''{set}x''{set}y''{set}z');  // x - y - z
        
$xyz array_unique(array_diff($x$y$z));
        
$this->assertEquals($countcount($xyz));
        foreach(
$xyz as $i) {
        
$i = (int)$i;
            
$this->assertTrue($this->redis->sismember('{set}k'$i));
        }

        
$this->redis->del('{set}x');  // x missing now
        
$count $this->redis->sDiffStore('{set}k''{set}x''{set}y''{set}z');  // x - y - z
        
$this->assertTrue($count === 0);

        
$this->redis->del('{set}y');  // x and y missing
        
$count $this->redis->sDiffStore('{set}k''{set}x''{set}y''{set}z');  // x - y - z
        
$this->assertTrue($count === 0);

        
$this->redis->del('{set}z');  // x, y, and z ALL missing
        
$count $this->redis->sDiffStore('{set}k''{set}x''{set}y''{set}z');  // x - y - z
        
$this->assertTrue($count === 0);
    }

    public function 
testlrange() {
        
$this->redis->del('list');
        
$this->redis->lPush('list''val');
        
$this->redis->lPush('list''val2');
        
$this->redis->lPush('list''val3');

        
// pos :   0     1     2
        // pos :  -3    -2    -1
        // list: [val3, val2, val]

        
$this->assertEquals($this->redis->lrange('list'00), ['val3']);
        
$this->assertEquals($this->redis->lrange('list'01), ['val3''val2']);
        
$this->assertEquals($this->redis->lrange('list'02), ['val3''val2''val']);
        
$this->assertEquals($this->redis->lrange('list'03), ['val3''val2''val']);

        
$this->assertEquals($this->redis->lrange('list'0, -1), ['val3''val2''val']);
        
$this->assertEquals($this->redis->lrange('list'0, -2), ['val3''val2']);
        
$this->assertEquals($this->redis->lrange('list', -2, -1), ['val2''val']);

        
$this->redis->del('list');
        
$this->assertEquals($this->redis->lrange('list'0, -1), []);
    }

    public function 
testdbSize() {
        
$this->assertTrue($this->redis->flushDB());
        
$this->redis->set('x''y');
        
$this->assertTrue($this->redis->dbSize() === 1);
    }

    public function 
testttl() {
        
$this->redis->set('x''y');
        
$this->redis->expire('x'5);
        
$ttl $this->redis->ttl('x');
        
$this->assertTrue($ttl && $ttl <= 5);

        
// A key with no TTL
        
$this->redis->del('x'); $this->redis->set('x''bar');
        
$this->assertEquals($this->redis->ttl('x'), -1);

        
// A key that doesn't exist (> 2.8 will return -2)
        
if(version_compare($this->version"2.8.0") >= 0) {
            
$this->redis->del('x');
            
$this->assertEquals($this->redis->ttl('x'), -2);
        }
    }

    public function 
testPersist() {
        
$this->redis->set('x''y');
        
$this->redis->expire('x'100);
        
$this->assertTrue(TRUE === $this->redis->persist('x'));     // true if there is a timeout
        
$this->assertTrue(-=== $this->redis->ttl('x'));       // -1: timeout has been removed.
        
$this->assertTrue(FALSE === $this->redis->persist('x'));    // false if there is no timeout
        
$this->redis->del('x');
        
$this->assertTrue(FALSE === $this->redis->persist('x'));    // false if the key doesn’t exist.
    
}

    public function 
testClient() {
        
/* CLIENT SETNAME */
        
$this->assertTrue($this->redis->client('setname''phpredis_unit_tests'));

        
/* CLIENT LIST */
        
$arr_clients $this->redis->client('list');
        
$this->assertTrue(is_array($arr_clients));

        
// Figure out which ip:port is us!
        
$str_addr NULL;
        foreach(
$arr_clients as $arr_client) {
            if(
$arr_client['name'] == 'phpredis_unit_tests') {
                
$str_addr $arr_client['addr'];
            }
        }

        
// We should have found our connection
        
$this->assertFalse(empty($str_addr));

        
/* CLIENT GETNAME */
        
$this->assertTrue($this->redis->client('getname'), 'phpredis_unit_tests');

        
/* CLIENT KILL -- phpredis will reconnect, so we can do this */
        
$this->assertTrue($this->redis->client('kill'$str_addr));
    }

    public function 
testSlowlog() {
        
// We don't really know what's going to be in the slowlog, but make sure
        // the command returns proper types when called in various ways
        
$this->assertTrue(is_array($this->redis->slowlog('get')));
        
$this->assertTrue(is_array($this->redis->slowlog('get'10)));
        
$this->assertTrue(is_int($this->redis->slowlog('len')));
        
$this->assertTrue($this->redis->slowlog('reset'));
        
$this->assertFalse($this->redis->slowlog('notvalid'));
    }

    public function 
testWait() {
        
// Closest we can check based on redis commmit history
        
if(version_compare($this->version'2.9.11') < 0) {
            
$this->markTestSkipped();
            return;
        }

        
// We could have slaves here, so determine that
        
$arr_slaves $this->redis->info();
        
$i_slaves   $arr_slaves['connected_slaves'];

        
// Send a couple commands
        
$this->redis->set('wait-foo''over9000');
        
$this->redis->set('wait-bar''revo9000');

        
// Make sure we get the right replication count
        
$this->assertEquals($this->redis->wait($i_slaves100), $i_slaves);

        
// Pass more slaves than are connected
        
$this->redis->set('wait-foo','over9000');
        
$this->redis->set('wait-bar','revo9000');
        
$this->assertTrue($this->redis->wait($i_slaves+1100) < $i_slaves+1);

        
// Make sure when we pass with bad arguments we just get back false
        
$this->assertFalse($this->redis->wait(-1, -1));
        
$this->assertFalse($this->redis->wait(-120));
    }

    public function 
testInfo() {
        foreach ([
falsetrue] as $boo_multi) {
            if (
$boo_multi) {
                
$this->redis->multi();
                
$this->redis->info();
                
$info $this->redis->exec();
                
$info $info[0];
            } else {
                
$info $this->redis->info();
            }

            
$keys = [
                
"redis_version",
                
"arch_bits",
                
"uptime_in_seconds",
                
"uptime_in_days",
                
"connected_clients",
                
"connected_slaves",
                
"used_memory",
                
"total_connections_received",
                
"total_commands_processed",
                
"role"
            
];
            if (
version_compare($this->version"2.5.0") < 0) {
                
array_push($keys,
                    
"changes_since_last_save",
                    
"bgsave_in_progress",
                    
"last_save_time"
                
);
            } else {
                
array_push($keys,
                    
"rdb_changes_since_last_save",
                    
"rdb_bgsave_in_progress",
                    
"rdb_last_save_time"
                
);
            }

            foreach(
$keys as $k) {
                
$this->assertTrue(in_array($karray_keys($info)));
            }
        }
    }

    public function 
testInfoCommandStats() {

    
// INFO COMMANDSTATS is new in 2.6.0
    
if (version_compare($this->version"2.5.0") < 0) {
        
$this->markTestSkipped();
    }

    
$info $this->redis->info("COMMANDSTATS");

    
$this->assertTrue(is_array($info));
    if (
is_array($info)) {
        foreach(
$info as $k => $value) {
            
$this->assertTrue(strpos($k'cmdstat_') !== false);
        }
    }
    }

    public function 
testSelect() {
        
$this->assertFalse($this->redis->select(-1));
        
$this->assertTrue($this->redis->select(0));
    }

    public function 
testSwapDB() {
        if (
version_compare($this->version"4.0.0") < 0) {
            
$this->markTestSkipped();
        }

        
$this->assertTrue($this->redis->swapdb(01));
        
$this->assertTrue($this->redis->swapdb(01));
    }

    public function 
testMset() {
    
$this->redis->del('x''y''z');    // remove x y z
    
$this->assertTrue($this->redis->mset(['x' => 'a''y' => 'b''z' => 'c']));   // set x y z

    
$this->assertEquals($this->redis->mget(['x''y''z']), ['a''b''c']);    // check x y z

    
$this->redis->del('x');  // delete just x
    
$this->assertTrue($this->redis->mset(['x' => 'a''y' => 'b''z' => 'c']));   // set x y z
    
$this->assertEquals($this->redis->mget(['x''y''z']), ['a''b''c']);    // check x y z

    
$this->assertFalse($this->redis->mset([])); // set Ã¸ â†’ FALSE


    /*
     * Integer keys
     */

    // No prefix
    
$set_array = [-=> 'neg1', -=> 'neg2', -=> 'neg3'=> 'one'=> 'two''3' => 'three'];
    
$this->redis->del(array_keys($set_array));
    
$this->assertTrue($this->redis->mset($set_array));
    
$this->assertEquals($this->redis->mget(array_keys($set_array)), array_values($set_array));
    
$this->redis->del(array_keys($set_array));

    
// With a prefix
    
$this->redis->setOption(Redis::OPT_PREFIX'pfx:');
    
$this->redis->del(array_keys($set_array));
    
$this->assertTrue($this->redis->mset($set_array));
    
$this->assertEquals($this->redis->mget(array_keys($set_array)), array_values($set_array));
    
$this->redis->del(array_keys($set_array));
    
$this->redis->setOption(Redis::OPT_PREFIX'');
    }

    public function 
testMsetNX() {
        
$this->redis->del('x''y''z');    // remove x y z
        
$this->assertTrue(TRUE === $this->redis->msetnx(['x' => 'a''y' => 'b''z' => 'c']));    // set x y z

        
$this->assertEquals($this->redis->mget(['x''y''z']), ['a''b''c']);    // check x y z

        
$this->redis->del('x');  // delete just x
        
$this->assertTrue(FALSE === $this->redis->msetnx(['x' => 'A''y' => 'B''z' => 'C']));   // set x y z
        
$this->assertEquals($this->redis->mget(['x''y''z']), [FALSE'b''c']);  // check x y z

        
$this->assertFalse($this->redis->msetnx([])); // set Ã¸ â†’ FALSE
    
}

    public function 
testRpopLpush() {
        
// standard case.
        
$this->redis->del('{list}x''{list}y');
        
$this->redis->lpush('{list}x''abc');
        
$this->redis->lpush('{list}x''def');    // x = [def, abc]

        
$this->redis->lpush('{list}y''123');
        
$this->redis->lpush('{list}y''456');    // y = [456, 123]

        
$this->assertEquals($this->redis->rpoplpush('{list}x''{list}y'), 'abc');  // we RPOP x, yielding abc.
        
$this->assertEquals($this->redis->lrange('{list}x'0, -1), ['def']); // only def remains in x.
        
$this->assertEquals($this->redis->lrange('{list}y'0, -1), ['abc''456''123']);   // abc has been lpushed to y.

        // with an empty source, expecting no change.
        
$this->redis->del('{list}x''{list}y');
        
$this->assertTrue(FALSE === $this->redis->rpoplpush('{list}x''{list}y'));
        
$this->assertTrue([] === $this->redis->lrange('{list}x'0, -1));
        
$this->assertTrue([] === $this->redis->lrange('{list}y'0, -1));
    }

    public function 
testBRpopLpush() {
        
// standard case.
        
$this->redis->del('{list}x''{list}y');
        
$this->redis->lpush('{list}x''abc');
        
$this->redis->lpush('{list}x''def');    // x = [def, abc]

        
$this->redis->lpush('{list}y''123');
        
$this->redis->lpush('{list}y''456');    // y = [456, 123]

        
$this->assertEquals($this->redis->brpoplpush('{list}x''{list}y'1), 'abc');  // we RPOP x, yielding abc.
        
$this->assertEquals($this->redis->lrange('{list}x'0, -1), ['def']); // only def remains in x.
        
$this->assertEquals($this->redis->lrange('{list}y'0, -1), ['abc''456''123']);   // abc has been lpushed to y.

        // with an empty source, expecting no change.
        
$this->redis->del('{list}x''{list}y');
        
$this->assertTrue(FALSE === $this->redis->brpoplpush('{list}x''{list}y'1));
        
$this->assertTrue([] === $this->redis->lrange('{list}x'0, -1));
        
$this->assertTrue([] === $this->redis->lrange('{list}y'0, -1));
    }

    public function 
testZAddFirstArg() {

        
$this->redis->del('key');

        
$zsetName 100// not a string!
        
$this->assertTrue(=== $this->redis->zAdd($zsetName0'val0'));
        
$this->assertTrue(=== $this->redis->zAdd($zsetName1'val1'));

        
$this->assertTrue(['val0''val1'] === $this->redis->zRange($zsetName0, -1));
    }

    public function 
testZX() {
        
$this->redis->del('key');

        
$this->assertTrue([] === $this->redis->zRange('key'0, -1));
        
$this->assertTrue([] === $this->redis->zRange('key'0, -1true));

        
$this->assertTrue(=== $this->redis->zAdd('key'0'val0'));
        
$this->assertTrue(=== $this->redis->zAdd('key'2'val2'));
        
$this->assertTrue(=== $this->redis->zAdd('key'4'val4'5'val5')); // multiple parameters
        
if (version_compare($this->version"3.0.2") < 0) {
            
$this->assertTrue(=== $this->redis->zAdd('key'1'val1'));
            
$this->assertTrue(=== $this->redis->zAdd('key'3'val3'));
        } else {
            
$this->assertTrue(=== $this->redis->zAdd('key', [], 1'val1')); // empty options
            
$this->assertTrue(=== $this->redis->zAdd('key', ['nx'], 3'val3')); // nx option
            
$this->assertTrue(=== $this->redis->zAdd('key', ['xx'], 3'val3')); // xx option
        
}

        
$this->assertTrue(['val0''val1''val2''val3''val4''val5'] === $this->redis->zRange('key'0, -1));

        
// withscores
        
$ret $this->redis->zRange('key'0, -1true);
        
$this->assertTrue(count($ret) == 6);
        
$this->assertTrue($ret['val0'] == 0);
        
$this->assertTrue($ret['val1'] == 1);
        
$this->assertTrue($ret['val2'] == 2);
        
$this->assertTrue($ret['val3'] == 3);
        
$this->assertTrue($ret['val4'] == 4);
        
$this->assertTrue($ret['val5'] == 5);

        
$this->assertTrue(=== $this->redis->zRem('key''valX'));
        
$this->assertTrue(=== $this->redis->zRem('key''val3'));
        
$this->assertTrue(=== $this->redis->zRem('key''val4'));
        
$this->assertTrue(=== $this->redis->zRem('key''val5'));

        
$this->assertTrue(['val0''val1''val2'] === $this->redis->zRange('key'0, -1));

        
// zGetReverseRange

        
$this->assertTrue(=== $this->redis->zAdd('key'3'val3'));
        
$this->assertTrue(=== $this->redis->zAdd('key'3'aal3'));

        
$zero_to_three $this->redis->zRangeByScore('key'03);
        
$this->assertTrue(['val0''val1''val2''aal3''val3'] === $zero_to_three || ['val0''val1''val2''val3''aal3'] === $zero_to_three);

        
$three_to_zero $this->redis->zRevRangeByScore('key'30);
        
$this->assertTrue(array_reverse(['val0''val1''val2''aal3''val3']) === $three_to_zero || array_reverse(['val0''val1''val2''val3''aal3']) === $three_to_zero);

        
$this->assertTrue(=== $this->redis->zCount('key'03));

        
// withscores
        
$this->redis->zRem('key''aal3');
        
$zero_to_three $this->redis->zRangeByScore('key'03, ['withscores' => TRUE]);
        
$this->assertTrue(['val0' => 0'val1' => 1'val2' => 2'val3' => 3] == $zero_to_three);
        
$this->assertTrue(=== $this->redis->zCount('key'03));

        
// limit
        
$this->assertTrue(['val0'] === $this->redis->zRangeByScore('key'03, ['limit' => [01]]));
        
$this->assertTrue(['val0''val1'] === $this->redis->zRangeByScore('key'03, ['limit' => [02]]));
        
$this->assertTrue(['val1''val2'] === $this->redis->zRangeByScore('key'03, ['limit' => [12]]));
        
$this->assertTrue(['val0''val1'] === $this->redis->zRangeByScore('key'01, ['limit' => [0100]]));

        
// limits as references
        
$limit = [0100];
        foreach (
$limit as &$val) {}
        
$this->assertTrue(['val0''val1'] === $this->redis->zRangeByScore('key'01, ['limit' => $limit]));

        
$this->assertTrue(['val3'] === $this->redis->zRevRangeByScore('key'30, ['limit' => [01]]));
        
$this->assertTrue(['val3''val2'] === $this->redis->zRevRangeByScore('key'30, ['limit' => [02]]));
        
$this->assertTrue(['val2''val1'] === $this->redis->zRevRangeByScore('key'30, ['limit' => [12]]));
        
$this->assertTrue(['val1''val0'] === $this->redis->zRevRangeByScore('key'10, ['limit' => [0100]]));

        
$this->assertTrue(=== $this->redis->zCard('key'));
        
$this->assertTrue(1.0 === $this->redis->zScore('key''val1'));
        
$this->assertFalse($this->redis->zScore('key''val'));
        
$this->assertFalse($this->redis->zScore(32));

        
// with () and +inf, -inf
        
$this->redis->del('zset');
        
$this->redis->zAdd('zset'1'foo');
        
$this->redis->zAdd('zset'2'bar');
        
$this->redis->zAdd('zset'3'biz');
        
$this->redis->zAdd('zset'4'foz');
        
$this->assertTrue(['foo' => 1'bar' => 2'biz' => 3'foz' => 4] == $this->redis->zRangeByScore('zset''-inf''+inf', ['withscores' => TRUE]));
        
$this->assertTrue(['foo' => 1'bar' => 2] == $this->redis->zRangeByScore('zset'12, ['withscores' => TRUE]));
        
$this->assertTrue(['bar' => 2] == $this->redis->zRangeByScore('zset''(1'2, ['withscores' => TRUE]));
        
$this->assertTrue([] == $this->redis->zRangeByScore('zset''(1''(2', ['withscores' => TRUE]));

        
$this->assertTrue(== $this->redis->zCount('zset''-inf''+inf'));
        
$this->assertTrue(== $this->redis->zCount('zset'12));
        
$this->assertTrue(== $this->redis->zCount('zset''(1'2));
        
$this->assertTrue(== $this->redis->zCount('zset''(1''(2'));


        
// zincrby
        
$this->redis->del('key');
        
$this->assertTrue(1.0 === $this->redis->zIncrBy('key'1'val1'));
        
$this->assertTrue(1.0 === $this->redis->zScore('key''val1'));
        
$this->assertTrue(2.5 === $this->redis->zIncrBy('key'1.5'val1'));
        
$this->assertTrue(2.5 === $this->redis->zScore('key''val1'));

        
// zUnionStore
        
$this->redis->del('{zset}1');
        
$this->redis->del('{zset}2');
        
$this->redis->del('{zset}3');
        
$this->redis->del('{zset}U');

        
$this->redis->zAdd('{zset}1'0'val0');
        
$this->redis->zAdd('{zset}1'1'val1');

        
$this->redis->zAdd('{zset}2'2'val2');
        
$this->redis->zAdd('{zset}2'3'val3');

        
$this->redis->zAdd('{zset}3'4'val4');
        
$this->redis->zAdd('{zset}3'5'val5');

        
$this->assertTrue(=== $this->redis->zUnionStore('{zset}U', ['{zset}1''{zset}3']));
        
$this->assertTrue(['val0''val1''val4''val5'] === $this->redis->zRange('{zset}U'0, -1));

        
// Union on non existing keys
        
$this->redis->del('{zset}U');
        
$this->assertTrue(=== $this->redis->zUnionStore('{zset}U', ['{zset}X''{zset}Y']));
        
$this->assertTrue([] === $this->redis->zRange('{zset}U'0, -1));

        
// !Exist U Exist â†’ copy of existing zset.
        
$this->redis->del('{zset}U''X');
        
$this->assertTrue(=== $this->redis->zUnionStore('{zset}U', ['{zset}1''{zset}X']));

        
// test weighted zUnion
        
$this->redis->del('{zset}Z');
        
$this->assertTrue(=== $this->redis->zUnionStore('{zset}Z', ['{zset}1''{zset}2'], [11]));
        
$this->assertTrue(['val0''val1''val2''val3'] === $this->redis->zRange('{zset}Z'0, -1));

        
$this->redis->zRemRangeByScore('{zset}Z'010);
        
$this->assertTrue(=== $this->redis->zUnionStore('{zset}Z', ['{zset}1''{zset}2'], [51]));
        
$this->assertTrue(['val0''val2''val3''val1'] === $this->redis->zRange('{zset}Z'0, -1));

        
$this->redis->del('{zset}1');
        
$this->redis->del('{zset}2');
        
$this->redis->del('{zset}3');

        
//test zUnion with weights and aggegration function
        
$this->redis->zadd('{zset}1'1'duplicate');
        
$this->redis->zadd('{zset}2'2'duplicate');
        
$this->redis->zUnionStore('{zset}U', ['{zset}1','{zset}2'], [1,1], 'MIN');
        
$this->assertTrue($this->redis->zScore('{zset}U''duplicate')===1.0);
        
$this->redis->del('{zset}U');

        
//now test zUnion *without* weights but with aggregrate function
        
$this->redis->zUnionStore('{zset}U', ['{zset}1','{zset}2'], null'MIN');
        
$this->assertTrue($this->redis->zScore('{zset}U''duplicate')===1.0);
        
$this->redis->del('{zset}U''{zset}1''{zset}2');

        
// test integer and float weights (GitHub issue #109).
        
$this->redis->del('{zset}1''{zset}2''{zset}3');

        
$this->redis->zadd('{zset}1'1'one');
        
$this->redis->zadd('{zset}1'2'two');
        
$this->redis->zadd('{zset}2'1'one');
        
$this->redis->zadd('{zset}2'2'two');
        
$this->redis->zadd('{zset}2'3'three');

        
$this->assertTrue($this->redis->zUnionStore('{zset}3', ['{zset}1''{zset}2'], [23.0]) === 3);

        
$this->redis->del('{zset}1');
        
$this->redis->del('{zset}2');
        
$this->redis->del('{zset}3');

        
// Test 'inf', '-inf', and '+inf' weights (GitHub issue #336)
        
$this->redis->zadd('{zset}1'1'one'2'two'3'three');
        
$this->redis->zadd('{zset}2'3'three'4'four'5'five');

        
// Make sure phpredis handles these weights
        
$this->assertTrue($this->redis->zUnionStore('{zset}3', ['{zset}1','{zset}2'], [1'inf'])  === 5);
        
$this->assertTrue($this->redis->zUnionStore('{zset}3', ['{zset}1','{zset}2'], [1'-inf']) === 5);
        
$this->assertTrue($this->redis->zUnionStore('{zset}3', ['{zset}1','{zset}2'], [1'+inf']) === 5);

        
// Now, confirm that they're being sent, and that it works
        
$arr_weights = ['inf','-inf','+inf'];

        foreach(
$arr_weights as $str_weight) {
            
$r $this->redis->zUnionStore('{zset}3', ['{zset}1','{zset}2'], [1,$str_weight]);
            
$this->assertTrue($r===5);
            
$r $this->redis->zrangebyscore('{zset}3''(-inf''(inf',['withscores'=>true]);
            
$this->assertTrue(count($r)===2);
            
$this->assertTrue(isset($r['one']));
            
$this->assertTrue(isset($r['two']));
        }

        
$this->redis->del('{zset}1','{zset}2','{zset}3');

        
$this->redis->zadd('{zset}1'2000.1'one');
        
$this->redis->zadd('{zset}1'3000.1'two');
        
$this->redis->zadd('{zset}1'4000.1'three');

        
$ret $this->redis->zRange('{zset}1'0, -1TRUE);
        
$this->assertTrue(count($ret) === 3);
        
$retValues array_keys($ret);

        
$this->assertTrue(['one''two''three'] === $retValues);

        
// + 0 converts from string to float OR integer
        
$this->assertTrue(is_float($ret['one'] + 0));
        
$this->assertTrue(is_float($ret['two'] + 0));
        
$this->assertTrue(is_float($ret['three'] + 0));

        
$this->redis->del('{zset}1');

        
// ZREMRANGEBYRANK
        
$this->redis->zAdd('{zset}1'1'one');
        
$this->redis->zAdd('{zset}1'2'two');
        
$this->redis->zAdd('{zset}1'3'three');
        
$this->assertTrue(=== $this->redis->zremrangebyrank('{zset}1'01));
        
$this->assertTrue(['three' => 3] == $this->redis->zRange('{zset}1'0, -1TRUE));

        
$this->redis->del('{zset}1');

        
// zInterStore

        
$this->redis->zAdd('{zset}1'0'val0');
        
$this->redis->zAdd('{zset}1'1'val1');
        
$this->redis->zAdd('{zset}1'3'val3');

        
$this->redis->zAdd('{zset}2'2'val1');
        
$this->redis->zAdd('{zset}2'3'val3');

        
$this->redis->zAdd('{zset}3'4'val3');
        
$this->redis->zAdd('{zset}3'5'val5');

        
$this->redis->del('{zset}I');
        
$this->assertTrue(=== $this->redis->zInterStore('{zset}I', ['{zset}1''{zset}2']));
        
$this->assertTrue(['val1''val3'] === $this->redis->zRange('{zset}I'0, -1));

        
// Union on non existing keys
        
$this->assertTrue(=== $this->redis->zInterStore('{zset}X', ['{zset}X''{zset}Y']));
        
$this->assertTrue([] === $this->redis->zRange('{zset}X'0, -1));

        
// !Exist U Exist
        
$this->assertTrue(=== $this->redis->zInterStore('{zset}Y', ['{zset}1''{zset}X']));
        
$this->assertTrue([] === $this->redis->zRange('keyY'0, -1));


        
// test weighted zInterStore
        
$this->redis->del('{zset}1');
        
$this->redis->del('{zset}2');
        
$this->redis->del('{zset}3');

        
$this->redis->zAdd('{zset}1'0'val0');
        
$this->redis->zAdd('{zset}1'1'val1');
        
$this->redis->zAdd('{zset}1'3'val3');


        
$this->redis->zAdd('{zset}2'2'val1');
        
$this->redis->zAdd('{zset}2'1'val3');

        
$this->redis->zAdd('{zset}3'7'val1');
        
$this->redis->zAdd('{zset}3'3'val3');

        
$this->redis->del('{zset}I');
        
$this->assertTrue(=== $this->redis->zInterStore('{zset}I', ['{zset}1''{zset}2'], [11]));
        
$this->assertTrue(['val1''val3'] === $this->redis->zRange('{zset}I'0, -1));

        
$this->redis->del('{zset}I');
        
$this->assertTrue=== $this->redis->zInterStore('{zset}I', ['{zset}1''{zset}2''{zset}3'], [151], 'min'));
        
$this->assertTrue(['val1''val3'] === $this->redis->zRange('{zset}I'0, -1));
        
$this->redis->del('{zset}I');
        
$this->assertTrue=== $this->redis->zInterStore('{zset}I', ['{zset}1''{zset}2''{zset}3'], [151], 'max'));
        
$this->assertTrue(['val3''val1'] === $this->redis->zRange('{zset}I'0, -1));

        
$this->redis->del('{zset}I');
        
$this->assertTrue(=== $this->redis->zInterStore('{zset}I', ['{zset}1''{zset}2''{zset}3'], null'max'));
        
$this->assertTrue($this->redis->zScore('{zset}I''val1') === floatval(7));

        
// zrank, zrevrank
        
$this->redis->del('z');
        
$this->redis->zadd('z'1'one');
        
$this->redis->zadd('z'2'two');
        
$this->redis->zadd('z'5'five');

        
$this->assertTrue(=== $this->redis->zRank('z''one'));
        
$this->assertTrue(=== $this->redis->zRank('z''two'));
        
$this->assertTrue(=== $this->redis->zRank('z''five'));

        
$this->assertTrue(=== $this->redis->zRevRank('z''one'));
        
$this->assertTrue(=== $this->redis->zRevRank('z''two'));
        
$this->assertTrue(=== $this->redis->zRevRank('z''five'));
    }

    public function 
testZRangeScoreArg() {
        
$this->redis->del('{z}');

        
$arr_mems = ['one' => 1.0'two' => 2.0'three' => 3.0];
        foreach (
$arr_mems as $str_mem => $score) {
            
$this->redis->zAdd('{z}'$score$str_mem);
        }

        
/* Verify we can pass true and ['withscores' => true] */
        
$this->assertEquals($arr_mems$this->redis->zRange('{z}'0, -1true));
        
$this->assertEquals($arr_mems$this->redis->zRange('{z}'0, -1, ['withscores' => true]));
    }

    public function 
testZRangeByLex() {
        
/* ZRANGEBYLEX available on versions >= 2.8.9 */
        
if(version_compare($this->version"2.8.9") < 0) {
            
$this->MarkTestSkipped();
            return;
        }

        
$this->redis->del('key');
        foreach(
range('a''g') as $c) {
            
$this->redis->zAdd('key'0$c);
        }

        
$this->assertEquals($this->redis->zRangeByLex('key''-''[c'), ['a''b''c']);
        
$this->assertEquals($this->redis->zRangeByLex('key''(e''+'), ['f''g']);

        
// with limit offset
        
$this->assertEquals($this->redis->zRangeByLex('key''-''[c'12), ['b''c'] );
        
$this->assertEquals($this->redis->zRangeByLex('key''-''(c'12), ['b']);
    }

    public function 
testZLexCount() {
        if (
version_compare($this->version"2.8.9") < 0) {
            
$this->MarkTestSkipped();
            return;
        }

        
$this->redis->del('key');
        foreach (
range('a''g') as $c) {
            
$entries[] = $c;
            
$this->redis->zAdd('key'0$c);
        }

        
/* Special -/+ values */
        
$this->assertEquals($this->redis->zLexCount('key''-''-'), 0);
        
$this->assertEquals($this->redis->zLexCount('key''-''+'), count($entries));

        
/* Verify invalid arguments return FALSE */
        
$this->assertFalse(@$this->redis->zLexCount('key''[a''bad'));
        
$this->assertFalse(@$this->redis->zLexCount('key''bad''[a'));

        
/* Now iterate through */
        
$start $entries[0];
        for (
$i 1$i count($entries); $i++) {
            
$end $entries[$i];
            
$this->assertEquals($this->redis->zLexCount('key'"[$start""[$end"), $i 1);
            
$this->assertEquals($this->redis->zLexCount('key'"[$start""($end"), $i);
            
$this->assertEquals($this->redis->zLexCount('key'"($start""($end"), $i 1);
        }
    }

    public function 
testZRemRangeByLex() {
        if (
version_compare($this->version"2.8.9") < 0) {
            
$this->MarkTestSkipped();
            return;
        }

        
$this->redis->del('key');
        
$this->redis->zAdd('key'0'a'0'b'0'c');
        
$this->assertEquals($this->redis->zRemRangeByLex('key''-''+'), 3);

        
$this->redis->zAdd('key'0'a'0'b'0'c');
        
$this->assertEquals($this->redis->zRemRangeByLex('key''[a''[c'), 3);

        
$this->redis->zAdd('key'0'a'0'b'0'c');
        
$this->assertEquals($this->redis->zRemRangeByLex('key''[a''(a'), 0);
        
$this->assertEquals($this->redis->zRemRangeByLex('key''(a''(c'), 1);
        
$this->assertEquals($this->redis->zRemRangeByLex('key''[a''[c'), 2);
    }

    public function 
testBZPop() {
        if (
version_compare($this->version"5.0.0") < 0) {
            
$this->MarkTestSkipped();
            return;
        }

        
$this->redis->del('{zs}1''{zs}2');
        
$this->redis->zAdd('{zs}1'0'a'1'b'2'c');
        
$this->redis->zAdd('{zs}2'3'A'4'B'5'D');

        
$this->assertEquals(Array('{zs}1''a''0'), $this->redis->bzPopMin('{zs}1''{zs}2'0));
        
$this->assertEquals(Array('{zs}1''c''2'), $this->redis->bzPopMax(Array('{zs}1''{zs}2'), 0));
        
$this->assertEquals(Array('{zs}2''A''3'), $this->redis->bzPopMin('{zs}2''{zs}1'0));

        
/* Verify timeout is being sent */
        
$this->redis->del('{zs}1''{zs}2');
        
$st microtime(true) * 1000;
        
$this->redis->bzPopMin('{zs}1''{zs}2'1);
        
$et microtime(true) * 1000;
        
$this->assertTrue($et $st 100);
    }

    public function 
testZPop() {
        if (
version_compare($this->version"5.0.0") < 0) {
            
$this->MarkTestSkipped();
            return;
        }

        
// zPopMax and zPopMin without a COUNT argument
        
$this->redis->del('key');
        
$this->redis->zAdd('key'0'a'1'b'2'c'3'd'4'e');
        
$this->assertTrue(array('e' => 4.0) === $this->redis->zPopMax('key'));
        
$this->assertTrue(array('a' => 0.0) === $this->redis->zPopMin('key'));

        
// zPopMax with a COUNT argument
        
$this->redis->del('key');
        
$this->redis->zAdd('key'0'a'1'b'2'c'3'd'4'e');
        
$this->assertTrue(array('e' => 4.0'd' => 3.0'c' => 2.0) === $this->redis->zPopMax('key'3));

        
// zPopMin with a COUNT argument
        
$this->redis->del('key');
        
$this->redis->zAdd('key'0'a'1'b'2'c'3'd'4'e');
        
$this->assertTrue(array('a' => 0.0'b' => 1.0'c' => 2.0) === $this->redis->zPopMin('key'3));
    }

    public function 
testHashes() {
        
$this->redis->del('h''key');
        
$this->assertTrue(=== $this->redis->hLen('h'));
        
$this->assertTrue(=== $this->redis->hSet('h''a''a-value'));
        
$this->assertTrue(=== $this->redis->hLen('h'));
        
$this->assertTrue(=== $this->redis->hSet('h''b''b-value'));
        
$this->assertTrue(=== $this->redis->hLen('h'));

        
$this->assertTrue('a-value' === $this->redis->hGet('h''a'));  // simple get
        
$this->assertTrue('b-value' === $this->redis->hGet('h''b'));  // simple get

        
$this->assertTrue(=== $this->redis->hSet('h''a''another-value')); // replacement
        
$this->assertTrue('another-value' === $this->redis->hGet('h''a'));    // get the new value

        
$this->assertTrue('b-value' === $this->redis->hGet('h''b'));  // simple get
        
$this->assertTrue(FALSE === $this->redis->hGet('h''c'));  // unknown hash member
        
$this->assertTrue(FALSE === $this->redis->hGet('key''c'));    // unknownkey

        // hDel
        
$this->assertTrue(=== $this->redis->hDel('h''a')); // 1 on success
        
$this->assertTrue(=== $this->redis->hDel('h''a')); // 0 on failure

        
$this->redis->del('h');
        
$this->redis->hSet('h''x''a');
        
$this->redis->hSet('h''y''b');
        
$this->assertTrue(=== $this->redis->hDel('h''x''y')); // variadic

        // hsetnx
        
$this->redis->del('h');
        
$this->assertTrue(TRUE === $this->redis->hSetNx('h''x''a'));
        
$this->assertTrue(TRUE === $this->redis->hSetNx('h''y''b'));
        
$this->assertTrue(FALSE === $this->redis->hSetNx('h''x''?'));
        
$this->assertTrue(FALSE === $this->redis->hSetNx('h''y''?'));
        
$this->assertTrue('a' === $this->redis->hGet('h''x'));
        
$this->assertTrue('b' === $this->redis->hGet('h''y'));

        
// keys
        
$keys $this->redis->hKeys('h');
        
$this->assertTrue($keys === ['x''y'] || $keys === ['y''x']);

        
// values
        
$values $this->redis->hVals('h');
        
$this->assertTrue($values === ['a''b'] || $values === ['b''a']);

        
// keys + values
        
$all $this->redis->hGetAll('h');
        
$this->assertTrue($all === ['x' => 'a''y' => 'b'] || $all === ['y' => 'b''x' => 'a']);

        
// hExists
        
$this->assertTrue(TRUE === $this->redis->hExists('h''x'));
        
$this->assertTrue(TRUE === $this->redis->hExists('h''y'));
        
$this->assertTrue(FALSE === $this->redis->hExists('h''w'));
        
$this->redis->del('h');
        
$this->assertTrue(FALSE === $this->redis->hExists('h''x'));

        
// hIncrBy
        
$this->redis->del('h');
        
$this->assertTrue(=== $this->redis->hIncrBy('h''x'2));
        
$this->assertTrue(=== $this->redis->hIncrBy('h''x'1));
        
$this->assertTrue(=== $this->redis->hIncrBy('h''x', -1));
        
$this->assertTrue("2" === $this->redis->hGet('h''x'));
        
$this->assertTrue(PHP_INT_MAX === $this->redis->hIncrBy('h''x'PHP_INT_MAX-2));
        
$this->assertTrue("".PHP_INT_MAX === $this->redis->hGet('h''x'));

        
$this->redis->hSet('h''y''not-a-number');
        
$this->assertTrue(FALSE === $this->redis->hIncrBy('h''y'1));

        if (
version_compare($this->version"2.5.0") >= 0) {
            
// hIncrByFloat
            
$this->redis->del('h');
            
$this->assertTrue(1.5 === $this->redis->hIncrByFloat('h','x'1.5));
            
$this->assertTrue(3.0 === $this->redis->hincrByFloat('h','x'1.5));
            
$this->assertTrue(1.5 === $this->redis->hincrByFloat('h','x', -1.5));
            
$this->assertTrue(1000000000001.5 === $this->redis->hincrByFloat('h','x'1000000000000));

            
$this->redis->hset('h','y','not-a-number');
            
$this->assertTrue(FALSE === $this->redis->hIncrByFloat('h''y'1.5));
        }

        
// hmset
        
$this->redis->del('h');
        
$this->assertTrue(TRUE === $this->redis->hMset('h', ['x' => 123'y' => 456'z' => 'abc']));
        
$this->assertTrue('123' === $this->redis->hGet('h''x'));
        
$this->assertTrue('456' === $this->redis->hGet('h''y'));
        
$this->assertTrue('abc' === $this->redis->hGet('h''z'));
        
$this->assertTrue(FALSE === $this->redis->hGet('h''t'));

        
// hmget
        
$this->assertTrue(['x' => '123''y' => '456'] === $this->redis->hMget('h', ['x''y']));
        
$this->assertTrue(['z' => 'abc'] === $this->redis->hMget('h', ['z']));
        
$this->assertTrue(['x' => '123''t' => FALSE'y' => '456'] === $this->redis->hMget('h', ['x''t''y']));
        
$this->assertFalse([123 => 'x'] === $this->redis->hMget('h', [123]));
        
$this->assertTrue([123 => FALSE] === $this->redis->hMget('h', [123]));

        
// Test with an array populated with things we can't use as keys
        
$this->assertTrue($this->redis->hmget('h', [false,NULL,false]) === FALSE);

        
// Test with some invalid keys mixed in (which should just be ignored)
        
$this->assertTrue(['x'=>'123','y'=>'456','z'=>'abc'] === $this->redis->hMget('h',['x',null,'y','','z',false]));

        
// hmget/hmset with numeric fields
        
$this->redis->del('h');
        
$this->assertTrue(TRUE === $this->redis->hMset('h', [123 => 'x''y' => 456]));
        
$this->assertTrue('x' === $this->redis->hGet('h'123));
        
$this->assertTrue('x' === $this->redis->hGet('h''123'));
        
$this->assertTrue('456' === $this->redis->hGet('h''y'));
        
$this->assertTrue([123 => 'x''y' => '456'] === $this->redis->hMget('h', ['123''y']));

        
// references
        
$keys = [123'y'];
        foreach (
$keys as &$key) {}
        
$this->assertTrue([123 => 'x''y' => '456'] === $this->redis->hMget('h'$keys));

        
// check non-string types.
        
$this->redis->del('h1');
        
$this->assertTrue(TRUE === $this->redis->hMSet('h1', ['x' => 0'y' => [], 'z' => new stdclass(), 't' => NULL]));
        
$h1 $this->redis->hGetAll('h1');
        
$this->assertTrue('0' === $h1['x']);
        
$this->assertTrue('Array' === $h1['y']);
        
$this->assertTrue('Object' === $h1['z']);
        
$this->assertTrue('' === $h1['t']);

        
// hstrlen
        
if (version_compare($this->version'3.2.0') >= 0) {
            
$this->redis->del('h');
            
$this->assertTrue(=== $this->redis->hStrLen('h''x')); // key doesn't exist
            
$this->redis->hSet('h''foo''bar');
            
$this->assertTrue(=== $this->redis->hStrLen('h''x')); // field is not present in the hash
            
$this->assertTrue(=== $this->redis->hStrLen('h''foo'));
    }
    }

    public function 
testSetRange() {

        
$this->redis->del('key');
        
$this->redis->set('key''hello world');
        
$this->redis->setRange('key'6'redis');
        
$this->assertTrue('hello redis' === $this->redis->get('key'));
        
$this->redis->setRange('key'6'you'); // don't cut off the end
        
$this->assertTrue('hello youis' === $this->redis->get('key'));

        
$this->redis->set('key''hello world');
        
// $this->assertTrue(11 === $this->redis->setRange('key', -6, 'redis')); // works with negative offsets too! (disabled because not all versions support this)
        // $this->assertTrue('hello redis' === $this->redis->get('key'));

        // fill with zeros if needed
        
$this->redis->del('key');
        
$this->redis->setRange('key'6'foo');
        
$this->assertTrue("\x00\x00\x00\x00\x00\x00foo" === $this->redis->get('key'));
    }

    public function 
testObject() {
        
/* Version 3.0.0 (represented as >= 2.9.0 in redis info)  and moving
         * forward uses "embstr" instead of "raw" for small string values */
        
if (version_compare($this->version"2.9.0") < 0) {
            
$str_small_encoding "raw";
        } else {
            
$str_small_encoding "embstr";
        }

        
$this->redis->del('key');
        
$this->assertTrue($this->redis->object('encoding''key') === FALSE);
        
$this->assertTrue($this->redis->object('refcount''key') === FALSE);
        
$this->assertTrue($this->redis->object('idletime''key') === FALSE);

        
$this->redis->set('key''value');
        
$this->assertTrue($this->redis->object('encoding''key') === $str_small_encoding);
        
$this->assertTrue($this->redis->object('refcount''key') === 1);
        
$this->assertTrue($this->redis->object('idletime''key') === 0);

        
$this->redis->del('key');
        
$this->redis->lpush('key''value');

        
/* Newer versions of redis are going to encode lists as 'quicklists',
         * so 'quicklist' or 'ziplist' is valid here */
        
$str_encoding $this->redis->object('encoding''key');
        
$this->assertTrue($str_encoding === "ziplist" || $str_encoding === 'quicklist');

        
$this->assertTrue($this->redis->object('refcount''key') === 1);
        
$this->assertTrue($this->redis->object('idletime''key') === 0);

        
$this->redis->del('key');
        
$this->redis->sadd('key''value');
        
$this->assertTrue($this->redis->object('encoding''key') === "hashtable");
        
$this->assertTrue($this->redis->object('refcount''key') === 1);
        
$this->assertTrue($this->redis->object('idletime''key') === 0);

        
$this->redis->del('key');
        
$this->redis->sadd('key'42);
        
$this->redis->sadd('key'1729);
        
$this->assertTrue($this->redis->object('encoding''key') === "intset");
        
$this->assertTrue($this->redis->object('refcount''key') === 1);
        
$this->assertTrue($this->redis->object('idletime''key') === 0);

        
$this->redis->del('key');
        
$this->redis->lpush('key'str_repeat('A'pow(10,6))); // 1M elements, too big for a ziplist.

        
$str_encoding $this->redis->object('encoding''key');
        
$this->assertTrue($str_encoding === "linkedlist" || $str_encoding == "quicklist");

        
$this->assertTrue($this->redis->object('refcount''key') === 1);
        
$this->assertTrue($this->redis->object('idletime''key') === 0);
    }

    public function 
testMultiExec() {
        
$this->sequence(Redis::MULTI);
        
$this->differentType(Redis::MULTI);

        
// with prefix as well
        
$this->redis->setOption(Redis::OPT_PREFIX"test:");
        
$this->sequence(Redis::MULTI);
        
$this->differentType(Redis::MULTI);
        
$this->redis->setOption(Redis::OPT_PREFIX"");

        
$this->redis->set('x''42');

        
$this->assertTrue(TRUE === $this->redis->watch('x'));
        
$ret $this->redis->multi()->get('x')->exec();

        
// successful transaction
        
$this->assertTrue($ret === ['42']);
    }

    public function 
testFailedTransactions() {
        
$this->redis->set('x'42);

        
// failed transaction
        
$this->redis->watch('x');

        
$r $this->newInstance(); // new instance, modifying `x'.
        
$r->incr('x');

        
$ret $this->redis->multi()->get('x')->exec();
        
$this->assertTrue($ret === FALSE); // failed because another client changed our watched key between WATCH and EXEC.

        // watch and unwatch
        
$this->redis->watch('x');
        
$r->incr('x'); // other instance
        
$this->redis->unwatch(); // cancel transaction watch

        
$ret $this->redis->multi()->get('x')->exec();

        
$this->assertTrue($ret === ['44']); // succeeded since we've cancel the WATCH command.
    
}

    public function 
testPipeline() {
        if (!
$this->havePipeline()) {
            
$this->markTestSkipped();
        }

        
$this->sequence(Redis::PIPELINE);
        
$this->differentType(Redis::PIPELINE);

        
// with prefix as well
        
$this->redis->setOption(Redis::OPT_PREFIX"test:");
        
$this->sequence(Redis::PIPELINE);
        
$this->differentType(Redis::PIPELINE);
        
$this->redis->setOption(Redis::OPT_PREFIX"");
    }

    public function 
testPipelineMultiExec()
    {
        if (!
$this->havePipeline()) {
            
$this->markTestSkipped();
        }

        
$ret $this->redis->pipeline()->multi()->exec()->exec();
        
$this->assertTrue(is_array($ret));
        
$this->assertEquals(1count($ret)); // empty transaction

        
$ret $this->redis->pipeline()
            ->
ping()
            ->
multi()->set('x'42)->incr('x')->exec()
            ->
ping()
            ->
multi()->get('x')->del('x')->exec()
            ->
ping()
            ->
exec();
        
$this->assertTrue(is_array($ret));
        
$this->assertEquals(5count($ret)); // should be 5 atomic operations
    
}

    
/* Github issue #1211 (ignore redundant calls to pipeline or multi) */
    
public function testDoublePipeNoOp() {
        
/* Only the first pipeline should be honored */
        
for ($i 0$i 6$i++) {
            
$this->redis->pipeline();
        }

        
/* Set and get in our pipeline */
        
$this->redis->set('pipecount','over9000')->get('pipecount');

        
$data $this->redis->exec();
        
$this->assertEquals([true,'over9000'], $data);

        
/* Only the first MULTI should be honored */
        
for ($i 0$i 6$i++) {
            
$this->redis->multi();
        }

        
/* Set and get in our MULTI block */
        
$this->redis->set('multicount''over9000')->get('multicount');

        
$data $this->redis->exec();
        
$this->assertEquals([true'over9000'], $data);
    }

    public function 
testDiscard()
    {
        foreach ([
Redis::PIPELINERedis::MULTI] as $mode) {
            
/* start transaction */
            
$this->redis->multi($mode);

            
/* Set and get in our transaction */
            
$this->redis->set('pipecount','over9000')->get('pipecount');

            
/* first call closes transaction and clears commands queue */
            
$this->assertTrue($this->redis->discard());

            
/* next call fails because mode is ATOMIC */
            
$this->assertFalse($this->redis->discard());
        }
    }

    protected function 
sequence($mode) {
        
$ret $this->redis->multi($mode)
            ->
set('x'42)
            ->
type('x')
            ->
get('x')
            ->
exec();

        
$this->assertTrue(is_array($ret));
        
$i 0;
        
$this->assertTrue($ret[$i++] == TRUE);
        
$this->assertTrue($ret[$i++] === Redis::REDIS_STRING);
        
$this->assertTrue($ret[$i] === '42' || $ret[$i] === 42);

        
$serializer $this->redis->getOption(Redis::OPT_SERIALIZER);
        
$this->redis->setOption(Redis::OPT_SERIALIZERRedis::SERIALIZER_NONE); // testing incr, which doesn't work with the serializer
        
$ret $this->redis->multi($mode)
            ->
del('{key}1')
            ->
set('{key}1''value1')
            ->
get('{key}1')
            ->
getSet('{key}1''value2')
            ->
get('{key}1')
            ->
set('{key}2'4)
            ->
incr('{key}2')
            ->
get('{key}2')
            ->
decr('{key}2')
            ->
get('{key}2')
            ->
rename('{key}2''{key}3')
            ->
get('{key}3')
            ->
renameNx('{key}3''{key}1')
            ->
rename('{key}3''{key}2')
            ->
incrby('{key}2'5)
            ->
get('{key}2')
            ->
decrby('{key}2'5)
            ->
get('{key}2')
            ->
exec();

        
$i 0;
        
$this->assertTrue(is_array($ret));
        
$this->assertTrue(is_long($ret[$i++]));
        
$this->assertTrue($ret[$i++] == TRUE);
        
$this->assertTrue($ret[$i++] == 'value1');
        
$this->assertTrue($ret[$i++] == 'value1');
        
$this->assertTrue($ret[$i++] == 'value2');
        
$this->assertTrue($ret[$i++] == TRUE);
        
$this->assertTrue($ret[$i++] == 5);
        
$this->assertTrue($ret[$i++] == 5);
        
$this->assertTrue($ret[$i++] == 4);
        
$this->assertTrue($ret[$i++] == 4);
        
$this->assertTrue($ret[$i++] == TRUE);
        
$this->assertTrue($ret[$i++] == 4);
        
$this->assertTrue($ret[$i++] == FALSE);
        
$this->assertTrue($ret[$i++] == TRUE);
        
$this->assertTrue($ret[$i++] == TRUE);
        
$this->assertTrue($ret[$i++] == 9);
        
$this->assertTrue($ret[$i++] == TRUE);
        
$this->assertTrue($ret[$i++] == 4);
        
$this->assertTrue(count($ret) == $i);

        
$this->redis->setOption(Redis::OPT_SERIALIZER$serializer);

        
$ret $this->redis->multi($mode)
            ->
del('{key}1')
            ->
del('{key}2')
            ->
set('{key}1''val1')
            ->
setnx('{key}1''valX')
            ->
setnx('{key}2''valX')
            ->
exists('{key}1')
            ->
exists('{key}3')
            ->
exec();

        
$this->assertTrue(is_array($ret));
        
$this->assertTrue($ret[0] == TRUE);
        
$this->assertTrue($ret[1] == TRUE);
        
$this->assertTrue($ret[2] == TRUE);
        
$this->assertTrue($ret[3] == FALSE);
        
$this->assertTrue($ret[4] == TRUE);
        
$this->assertTrue($ret[5] == TRUE);
        
$this->assertTrue($ret[6] == FALSE);

        
// ttl, mget, mset, msetnx, expire, expireAt
        
$this->redis->del('key');
        
$ret $this->redis->multi($mode)
            ->
ttl('key')
            ->
mget(['{key}1''{key}2''{key}3'])
            ->
mset(['{key}3' => 'value3''{key}4' => 'value4'])
            ->
set('key''value')
            ->
expire('key'5)
            ->
ttl('key')
            ->
expireAt('key''0000')
            ->
exec();

        
$this->assertTrue(is_array($ret));
        
$i 0;
        
$ttl $ret[$i++];
        
$this->assertTrue($ttl === -|| $ttl === -2);
        
$this->assertTrue($ret[$i++] === ['val1''valX'FALSE]); // mget
        
$this->assertTrue($ret[$i++] === TRUE); // mset
        
$this->assertTrue($ret[$i++] === TRUE); // set
        
$this->assertTrue($ret[$i++] === TRUE); // expire
        
$this->assertTrue($ret[$i++] === 5);    // ttl
        
$this->assertTrue($ret[$i++] === TRUE); // expireAt
        
$this->assertTrue(count($ret) == $i);

        
$ret $this->redis->multi($mode)
            ->
set('{list}lkey''x')
            ->
set('{list}lDest''y')
            ->
del('{list}lkey''{list}lDest')
            ->
rpush('{list}lkey''lvalue')
            ->
lpush('{list}lkey''lvalue')
            ->
lpush('{list}lkey''lvalue')
            ->
lpush('{list}lkey''lvalue')
            ->
lpush('{list}lkey''lvalue')
            ->
lpush('{list}lkey''lvalue')
            ->
rpoplpush('{list}lkey''{list}lDest')
            ->
lrange('{list}lDest'0, -1)
            ->
lpop('{list}lkey')
            ->
llen('{list}lkey')
            ->
lrem('{list}lkey''lvalue'3)
            ->
llen('{list}lkey')
            ->
lIndex('{list}lkey'0)
            ->
lrange('{list}lkey'0, -1)
            ->
lSet('{list}lkey'1"newValue")    // check errors on key not exists
            
->lrange('{list}lkey'0, -1)
            ->
llen('{list}lkey')
            ->
exec();

        
$this->assertTrue(is_array($ret));
        
$i 0;
        
$this->assertTrue($ret[$i++] === TRUE); // SET
        
$this->assertTrue($ret[$i++] === TRUE); // SET
        
$this->assertTrue($ret[$i++] === 2); // deleting 2 keys
        
$this->assertTrue($ret[$i++] === 1); // rpush, now 1 element
        
$this->assertTrue($ret[$i++] === 2); // lpush, now 2 elements
        
$this->assertTrue($ret[$i++] === 3); // lpush, now 3 elements
        
$this->assertTrue($ret[$i++] === 4); // lpush, now 4 elements
        
$this->assertTrue($ret[$i++] === 5); // lpush, now 5 elements
        
$this->assertTrue($ret[$i++] === 6); // lpush, now 6 elements
        
$this->assertTrue($ret[$i++] === 'lvalue'); // rpoplpush returns the element: "lvalue"
        
$this->assertTrue($ret[$i++] === ['lvalue']); // lDest contains only that one element.
        
$this->assertTrue($ret[$i++] === 'lvalue'); // removing a second element from lkey, now 4 elements left â†“
        
$this->assertTrue($ret[$i++] === 4); // 4 elements left, after 2 pops.
        
$this->assertTrue($ret[$i++] === 3); // removing 3 elements, now 1 left.
        
$this->assertTrue($ret[$i++] === 1); // 1 element left
        
$this->assertTrue($ret[$i++] === "lvalue"); // this is the current head.
        
$this->assertTrue($ret[$i++] === ["lvalue"]); // this is the current list.
        
$this->assertTrue($ret[$i++] === FALSE); // updating a non-existent element fails.
        
$this->assertTrue($ret[$i++] === ["lvalue"]); // this is the current list.
        
$this->assertTrue($ret[$i++] === 1); // 1 element left
        
$this->assertTrue(count($ret) == $i);


        
$ret $this->redis->multi($mode)
            ->
del('{list}lkey''{list}lDest')
            ->
rpush('{list}lkey''lvalue')
            ->
lpush('{list}lkey''lvalue')
            ->
lpush('{list}lkey''lvalue')
            ->
rpoplpush('{list}lkey''{list}lDest')
            ->
lrange('{list}lDest'0, -1)
            ->
lpop('{list}lkey')
            ->
exec();
        
$this->assertTrue(is_array($ret));
        
$i 0;
        
$this->assertTrue($ret[$i++] <= 2); // deleted 0, 1, or 2 items
        
$this->assertTrue($ret[$i++] === 1); // 1 element in the list
        
$this->assertTrue($ret[$i++] === 2); // 2 elements in the list
        
$this->assertTrue($ret[$i++] === 3); // 3 elements in the list
        
$this->assertTrue($ret[$i++] === 'lvalue'); // rpoplpush returns the element: "lvalue"
        
$this->assertTrue($ret[$i++] === ['lvalue']); // rpoplpush returns the element: "lvalue"
        
$this->assertTrue($ret[$i++] === 'lvalue'); // pop returns the front element: "lvalue"
        
$this->assertTrue(count($ret) == $i);


        
$serializer $this->redis->getOption(Redis::OPT_SERIALIZER);
        
$this->redis->setOption(Redis::OPT_SERIALIZERRedis::SERIALIZER_NONE); // testing incr, which doesn't work with the serializer
        
$ret $this->redis->multi($mode)
            ->
del('{key}1')
            ->
set('{key}1''value1')
            ->
get('{key}1')
            ->
getSet('{key}1''value2')
            ->
get('{key}1')
            ->
set('{key}2'4)
            ->
incr('{key}2')
            ->
get('{key}2')
            ->
decr('{key}2')
            ->
get('{key}2')
            ->
rename('{key}2''{key}3')
            ->
get('{key}3')
            ->
renameNx('{key}3''{key}1')
            ->
rename('{key}3''{key}2')
            ->
incrby('{key}2'5)
            ->
get('{key}2')
            ->
decrby('{key}2'5)
            ->
get('{key}2')
            ->
set('{key}3''value3')
            ->
exec();

        
$i 0;
        
$this->assertTrue(is_array($ret));
        
$this->assertTrue(is_long($ret[$i]) && $ret[$i] <= 1); $i++;
        
$this->assertTrue($ret[$i++] == TRUE);
        
$this->assertTrue($ret[$i++] == 'value1');
        
$this->assertTrue($ret[$i++] == 'value1');
        
$this->assertTrue($ret[$i++] == 'value2');
        
$this->assertTrue($ret[$i++] == TRUE);
        
$this->assertTrue($ret[$i++] == 5);
        
$this->assertTrue($ret[$i++] == 5);
        
$this->assertTrue($ret[$i++] == 4);
        
$this->assertTrue($ret[$i++] == 4);
        
$this->assertTrue($ret[$i++] == TRUE);
        
$this->assertTrue($ret[$i++] == 4);
        
$this->assertTrue($ret[$i++] == FALSE);
        
$this->assertTrue($ret[$i++] == TRUE);
        
$this->assertTrue($ret[$i++] == TRUE);
        
$this->assertTrue($ret[$i++] == 9);
        
$this->assertTrue($ret[$i++] == TRUE);
        
$this->assertTrue($ret[$i++] == 4);
        
$this->assertTrue($ret[$i++]);
        
$this->redis->setOption(Redis::OPT_SERIALIZER$serializer);

        
$ret $this->redis->multi($mode)
            ->
del('{key}1')
            ->
del('{key}2')
            ->
del('{key}3')
            ->
set('{key}1''val1')
            ->
setnx('{key}1''valX')
            ->
setnx('{key}2''valX')
            ->
exists('{key}1')
            ->
exists('{key}3')
            ->
exec();

        
$this->assertTrue(is_array($ret));
        
$this->assertTrue($ret[0] == TRUE);
        
$this->assertTrue($ret[1] == TRUE);
        
$this->assertTrue($ret[2] == TRUE);
        
$this->assertTrue($ret[3] == TRUE);
        
$this->assertTrue($ret[4] == FALSE);
        
$this->assertTrue($ret[5] == TRUE);
        
$this->assertTrue($ret[6] == TRUE);
        
$this->assertTrue($ret[7] == FALSE);

        
// ttl, mget, mset, msetnx, expire, expireAt
        
$ret $this->redis->multi($mode)
            ->
ttl('key')
            ->
mget(['{key}1''{key}2''{key}3'])
            ->
mset(['{key}3' => 'value3''{key}4' => 'value4'])
            ->
set('key''value')
            ->
expire('key'5)
            ->
ttl('key')
            ->
expireAt('key''0000')
            ->
exec();
        
$i 0;
        
$this->assertTrue(is_array($ret));
        
$this->assertTrue(is_long($ret[$i++]));
        
$this->assertTrue(is_array($ret[$i]) && count($ret[$i]) === 3); // mget
        
$i++;
        
$this->assertTrue($ret[$i++] === TRUE); // mset always returns TRUE
        
$this->assertTrue($ret[$i++] === TRUE); // set always returns TRUE
        
$this->assertTrue($ret[$i++] === TRUE); // expire always returns TRUE
        
$this->assertTrue($ret[$i++] === 5); // TTL was just set.
        
$this->assertTrue($ret[$i++] === TRUE); // expireAt returns TRUE for an existing key
        
$this->assertTrue(count($ret) === $i);

        
// lists
        
$ret $this->redis->multi($mode)
            ->
del('{l}key''{l}Dest')
            ->
rpush('{l}key''lvalue')
            ->
lpush('{l}key''lvalue')
            ->
lpush('{l}key''lvalue')
            ->
lpush('{l}key''lvalue')
            ->
lpush('{l}key''lvalue')
            ->
lpush('{l}key''lvalue')
            ->
rpoplpush('{l}key''{l}Dest')
            ->
lrange('{l}Dest'0, -1)
            ->
lpop('{l}key')
            ->
llen('{l}key')
            ->
lrem('{l}key''lvalue'3)
            ->
llen('{l}key')
            ->
lIndex('{l}key'0)
            ->
lrange('{l}key'0, -1)
            ->
lSet('{l}key'1"newValue")    // check errors on missing key
            
->lrange('{l}key'0, -1)
            ->
llen('{l}key')
            ->
exec();

        
$this->assertTrue(is_array($ret));
        
$i 0;
        
$this->assertTrue($ret[$i] >= && $ret[$i] <= 2); // del
        
$i++;
        
$this->assertTrue($ret[$i++] === 1); // 1 value
        
$this->assertTrue($ret[$i++] === 2); // 2 values
        
$this->assertTrue($ret[$i++] === 3); // 3 values
        
$this->assertTrue($ret[$i++] === 4); // 4 values
        
$this->assertTrue($ret[$i++] === 5); // 5 values
        
$this->assertTrue($ret[$i++] === 6); // 6 values
        
$this->assertTrue($ret[$i++] === 'lvalue');
        
$this->assertTrue($ret[$i++] === ['lvalue']); // 1 value only in lDest
        
$this->assertTrue($ret[$i++] === 'lvalue'); // now 4 values left
        
$this->assertTrue($ret[$i++] === 4);
        
$this->assertTrue($ret[$i++] === 3); // removing 3 elements.
        
$this->assertTrue($ret[$i++] === 1); // length is now 1
        
$this->assertTrue($ret[$i++] === 'lvalue'); // this is the head
        
$this->assertTrue($ret[$i++] === ['lvalue']); // 1 value only in lkey
        
$this->assertTrue($ret[$i++] === FALSE); // can't set list[1] if we only have a single value in it.
        
$this->assertTrue($ret[$i++] === ['lvalue']); // the previous error didn't touch anything.
        
$this->assertTrue($ret[$i++] === 1); // the previous error didn't change the length
        
$this->assertTrue(count($ret) === $i);


        
// sets
        
$ret $this->redis->multi($mode)
            ->
del('{s}key1''{s}key2''{s}keydest''{s}keyUnion''{s}DiffDest')
            ->
sadd('{s}key1''sValue1')
            ->
sadd('{s}key1''sValue2')
            ->
sadd('{s}key1''sValue3')
            ->
sadd('{s}key1''sValue4')

            ->
sadd('{s}key2''sValue1')
            ->
sadd('{s}key2''sValue2')

            ->
scard('{s}key1')
            ->
srem('{s}key1''sValue2')
            ->
scard('{s}key1')
            ->
sMove('{s}key1''{s}key2''sValue4')
            ->
scard('{s}key2')
            ->
sismember('{s}key2''sValue4')
            ->
sMembers('{s}key1')
            ->
sMembers('{s}key2')
            ->
sInter('{s}key1''{s}key2')
            ->
sInterStore('{s}keydest''{s}key1''{s}key2')
            ->
sMembers('{s}keydest')
            ->
sUnion('{s}key2''{s}keydest')
            ->
sUnionStore('{s}keyUnion''{s}key2''{s}keydest')
            ->
sMembers('{s}keyUnion')
            ->
sDiff('{s}key1''{s}key2')
            ->
sDiffStore('{s}DiffDest''{s}key1''{s}key2')
            ->
sMembers('{s}DiffDest')
            ->
sPop('{s}key2')
            ->
scard('{s}key2')
            ->
exec();

        
$i 0;
        
$this->assertTrue(is_array($ret));
        
$this->assertTrue(is_long($ret[$i]) && $ret[$i] >= && $ret[$i] <= 5); $i++; // deleted at most 5 values.
        
$this->assertTrue($ret[$i++] === 1); // skey1 now has 1 element.
        
$this->assertTrue($ret[$i++] === 1); // skey1 now has 2 elements.
        
$this->assertTrue($ret[$i++] === 1); // skey1 now has 3 elements.
        
$this->assertTrue($ret[$i++] === 1); // skey1 now has 4 elements.

        
$this->assertTrue($ret[$i++] === 1); // skey2 now has 1 element.
        
$this->assertTrue($ret[$i++] === 1); // skey2 now has 2 elements.

        
$this->assertTrue($ret[$i++] === 4);
        
$this->assertTrue($ret[$i++] === 1); // we did remove that value.
        
$this->assertTrue($ret[$i++] === 3); // now 3 values only.
        
$this->assertTrue($ret[$i++] === TRUE); // the move did succeed.
        
$this->assertTrue($ret[$i++] === 3); // sKey2 now has 3 values.
        
$this->assertTrue($ret[$i++] === TRUE); // sKey2 does contain sValue4.
        
foreach(['sValue1''sValue3'] as $k) { // sKey1 contains sValue1 and sValue3.
            
$this->assertTrue(in_array($k$ret[$i]));
        }
        
$this->assertTrue(count($ret[$i++]) === 2);
        foreach([
'sValue1''sValue2''sValue4'] as $k) { // sKey2 contains sValue1, sValue2, and sValue4.
            
$this->assertTrue(in_array($k$ret[$i]));
        }
        
$this->assertTrue(count($ret[$i++]) === 3);
        
$this->assertTrue($ret[$i++] === ['sValue1']); // intersection
        
$this->assertTrue($ret[$i++] === 1); // intersection + store â†’ 1 value in the destination set.
        
$this->assertTrue($ret[$i++] === ['sValue1']); // sinterstore destination contents

        
foreach(['sValue1''sValue2''sValue4'] as $k) { // (skeydest U sKey2) contains sValue1, sValue2, and sValue4.
            
$this->assertTrue(in_array($k$ret[$i]));
        }
        
$this->assertTrue(count($ret[$i++]) === 3); // union size

        
$this->assertTrue($ret[$i++] === 3); // unionstore size
        
foreach(['sValue1''sValue2''sValue4'] as $k) { // (skeyUnion) contains sValue1, sValue2, and sValue4.
            
$this->assertTrue(in_array($k$ret[$i]));
        }
        
$this->assertTrue(count($ret[$i++]) === 3); // skeyUnion size

        
$this->assertTrue($ret[$i++] === ['sValue3']); // diff skey1, skey2 : only sValue3 is not shared.
        
$this->assertTrue($ret[$i++] === 1); // sdiffstore size == 1
        
$this->assertTrue($ret[$i++] === ['sValue3']); // contents of sDiffDest

        
$this->assertTrue(in_array($ret[$i++], ['sValue1''sValue2''sValue4'])); // we removed an element from sKey2
        
$this->assertTrue($ret[$i++] === 2); // sKey2 now has 2 elements only.

        
$this->assertTrue(count($ret) === $i);

        
// sorted sets
        
$ret $this->redis->multi($mode)
            ->
del('{z}key1''{z}key2''{z}key5''{z}Inter''{z}Union')
            ->
zadd('{z}key1'1'zValue1')
            ->
zadd('{z}key1'5'zValue5')
            ->
zadd('{z}key1'2'zValue2')
            ->
zRange('{z}key1'0, -1)
            ->
zRem('{z}key1''zValue2')
            ->
zRange('{z}key1'0, -1)
            ->
zadd('{z}key1'11'zValue11')
            ->
zadd('{z}key1'12'zValue12')
            ->
zadd('{z}key1'13'zValue13')
            ->
zadd('{z}key1'14'zValue14')
            ->
zadd('{z}key1'15'zValue15')
            ->
zRemRangeByScore('{z}key1'1113)
            ->
zrange('{z}key1'0, -1)
            ->
zRevRange('{z}key1'0, -1)
            ->
zRangeByScore('{z}key1'16)
            ->
zCard('{z}key1')
            ->
zScore('{z}key1''zValue15')
            ->
zadd('{z}key2'5'zValue5')
            ->
zadd('{z}key2'2'zValue2')
            ->
zInterStore('{z}Inter', ['{z}key1''{z}key2'])
            ->
zRange('{z}key1'0, -1)
            ->
zRange('{z}key2'0, -1)
            ->
zRange('{z}Inter'0, -1)
            ->
zUnionStore('{z}Union', ['{z}key1''{z}key2'])
            ->
zRange('{z}Union'0, -1)
            ->
zadd('{z}key5'5'zValue5')
            ->
zIncrBy('{z}key5'3'zValue5'// fix this
            
->zScore('{z}key5''zValue5')
            ->
zScore('{z}key5''unknown')
            ->
exec();

        
$i 0;
        
$this->assertTrue(is_array($ret));
        
$this->assertTrue(is_long($ret[$i]) && $ret[$i] >= && $ret[$i] <= 5); $i++; // deleting at most 5 keys
        
$this->assertTrue($ret[$i++] === 1);
        
$this->assertTrue($ret[$i++] === 1);
        
$this->assertTrue($ret[$i++] === 1);
        
$this->assertTrue($ret[$i++] === ['zValue1''zValue2''zValue5']);
        
$this->assertTrue($ret[$i++] === 1);
        
$this->assertTrue($ret[$i++] === ['zValue1''zValue5']);
        
$this->assertTrue($ret[$i++] === 1); // adding zValue11
        
$this->assertTrue($ret[$i++] === 1); // adding zValue12
        
$this->assertTrue($ret[$i++] === 1); // adding zValue13
        
$this->assertTrue($ret[$i++] === 1); // adding zValue14
        
$this->assertTrue($ret[$i++] === 1); // adding zValue15
        
$this->assertTrue($ret[$i++] === 3); // deleted zValue11, zValue12, zValue13
        
$this->assertTrue($ret[$i++] === ['zValue1''zValue5''zValue14''zValue15']);
        
$this->assertTrue($ret[$i++] === ['zValue15''zValue14''zValue5''zValue1']);
        
$this->assertTrue($ret[$i++] === ['zValue1''zValue5']);
        
$this->assertTrue($ret[$i++] === 4); // 4 elements
        
$this->assertTrue($ret[$i++] === 15.0);
        
$this->assertTrue($ret[$i++] === 1); // added value
        
$this->assertTrue($ret[$i++] === 1); // added value
        
$this->assertTrue($ret[$i++] === 1); // zinter only has 1 value
        
$this->assertTrue($ret[$i++] === ['zValue1''zValue5''zValue14''zValue15']); // {z}key1 contents
        
$this->assertTrue($ret[$i++] === ['zValue2''zValue5']); // {z}key2 contents
        
$this->assertTrue($ret[$i++] === ['zValue5']); // {z}inter contents
        
$this->assertTrue($ret[$i++] === 5); // {z}Union has 5 values (1,2,5,14,15)
        
$this->assertTrue($ret[$i++] === ['zValue1''zValue2''zValue5''zValue14''zValue15']); // {z}Union contents
        
$this->assertTrue($ret[$i++] === 1); // added value to {z}key5, with score 5
        
$this->assertTrue($ret[$i++] === 8.0); // incremented score by 3 â†’ it is now 8.
        
$this->assertTrue($ret[$i++] === 8.0); // current score is 8.
        
$this->assertTrue($ret[$i++] === FALSE); // score for unknown element.

        
$this->assertTrue(count($ret) === $i);

        
// hash
        
$ret $this->redis->multi($mode)
            ->
del('hkey1')
            ->
hset('hkey1''key1''value1')
            ->
hset('hkey1''key2''value2')
            ->
hset('hkey1''key3''value3')
            ->
hmget('hkey1', ['key1''key2''key3'])
            ->
hget('hkey1''key1')
            ->
hlen('hkey1')
            ->
hdel('hkey1''key2')
            ->
hdel('hkey1''key2')
            ->
hexists('hkey1''key2')
            ->
hkeys('hkey1')
            ->
hvals('hkey1')
            ->
hgetall('hkey1')
            ->
hset('hkey1''valn'1)
            ->
hset('hkey1''val-fail''non-string')
            ->
hget('hkey1''val-fail')
            ->
exec();

        
$i 0;
        
$this->assertTrue(is_array($ret));
        
$this->assertTrue($ret[$i++] <= 1); // delete
        
$this->assertTrue($ret[$i++] === 1); // added 1 element
        
$this->assertTrue($ret[$i++] === 1); // added 1 element
        
$this->assertTrue($ret[$i++] === 1); // added 1 element
        
$this->assertTrue($ret[$i++] === ['key1' => 'value1''key2' => 'value2''key3' => 'value3']); // hmget, 3 elements
        
$this->assertTrue($ret[$i++] === 'value1'); // hget
        
$this->assertTrue($ret[$i++] === 3); // hlen
        
$this->assertTrue($ret[$i++] === 1); // hdel succeeded
        
$this->assertTrue($ret[$i++] === 0); // hdel failed
        
$this->assertTrue($ret[$i++] === FALSE); // hexists didn't find the deleted key
        
$this->assertTrue($ret[$i] === ['key1''key3'] || $ret[$i] === ['key3''key1']); $i++; // hkeys
        
$this->assertTrue($ret[$i] === ['value1''value3'] || $ret[$i] === ['value3''value1']); $i++; // hvals
        
$this->assertTrue($ret[$i] === ['key1' => 'value1''key3' => 'value3'] || $ret[$i] === ['key3' => 'value3''key1' => 'value1']); $i++; // hgetall
        
$this->assertTrue($ret[$i++] === 1); // added 1 element
        
$this->assertTrue($ret[$i++] === 1); // added the element, so 1.
        
$this->assertTrue($ret[$i++] === 'non-string'); // hset succeeded
        
$this->assertTrue(count($ret) === $i);

        
$ret $this->redis->multi($mode// default to MULTI, not PIPELINE.
            
->del('test')
            ->
set('test''xyz')
            ->
get('test')
            ->
exec();
        
$i 0;
        
$this->assertTrue(is_array($ret));
        
$this->assertTrue($ret[$i++] <= 1); // delete
        
$this->assertTrue($ret[$i++] === TRUE); // added 1 element
        
$this->assertTrue($ret[$i++] === 'xyz');
        
$this->assertTrue(count($ret) === $i);

        
// GitHub issue 78
        
$this->redis->del('test');
        for(
$i 1$i <= 5$i++)
            
$this->redis->zadd('test'$i, (string)$i);

        
$result $this->redis->multi($mode)
            ->
zscore('test'"1")
            ->
zscore('test'"6")
            ->
zscore('test'"8")
            ->
zscore('test'"2")
            ->
exec();

        
$this->assertTrue($result === [1.0FALSEFALSE2.0]);
    }

    protected function 
differentType($mode) {

        
// string
        
$key '{hash}string';
        
$dkey '{hash}' __FUNCTION__;

        
$ret $this->redis->multi($mode)
            ->
del($key)
            ->
set($key'value')

            
// lists I/F
            
->rPush($key'lvalue')
            ->
lPush($key'lvalue')
            ->
lLen($key)
            ->
lPop($key)
            ->
lrange($key0, -1)
            ->
lTrim($key01)
            ->
lIndex($key0)
            ->
lSet($key0"newValue")
            ->
lrem($key'lvalue'1)
            ->
lPop($key)
            ->
rPop($key)
            ->
rPoplPush($key$dkey 'lkey1')

            
// sets I/F
            
->sAdd($key'sValue1')
            ->
srem($key'sValue1')
            ->
sPop($key)
            ->
sMove($key$dkey 'skey1''sValue1')

            ->
scard($key)
            ->
sismember($key'sValue1')
            ->
sInter($key$dkey 'skey2')

            ->
sUnion($key$dkey 'skey4')
            ->
sDiff($key$dkey 'skey7')
            ->
sMembers($key)
            ->
sRandMember($key)

            
// sorted sets I/F
            
->zAdd($key1'zValue1')
            ->
zRem($key'zValue1')
            ->
zIncrBy($key1'zValue1')
            ->
zRank($key'zValue1')
            ->
zRevRank($key'zValue1')
            ->
zRange($key0, -1)
            ->
zRevRange($key0, -1)
            ->
zRangeByScore($key12)
            ->
zCount($key0, -1)
            ->
zCard($key)
            ->
zScore($key'zValue1')
            ->
zRemRangeByRank($key12)
            ->
zRemRangeByScore($key12)

            
// hash I/F
            
->hSet($key'key1''value1')
            ->
hGet($key'key1')
            ->
hMGet($key, ['key1'])
            ->
hMSet($key, ['key1' => 'value1'])
            ->
hIncrBy($key'key2'1)
            ->
hExists($key'key2')
            ->
hDel($key'key2')
            ->
hLen($key)
            ->
hKeys($key)
            ->
hVals($key)
            ->
hGetAll($key)

            ->
exec();

        
$i 0;
        
$this->assertTrue(is_array($ret));
        
$this->assertTrue(is_long($ret[$i++])); // delete
        
$this->assertTrue($ret[$i++] === TRUE); // set

        
$this->assertTrue($ret[$i++] === FALSE); // rpush
        
$this->assertTrue($ret[$i++] === FALSE); // lpush
        
$this->assertTrue($ret[$i++] === FALSE); // llen
        
$this->assertTrue($ret[$i++] === FALSE); // lpop
        
$this->assertTrue($ret[$i++] === FALSE); // lrange
        
$this->assertTrue($ret[$i++] === FALSE); // ltrim
        
$this->assertTrue($ret[$i++] === FALSE); // lindex
        
$this->assertTrue($ret[$i++] === FALSE); // lset
        
$this->assertTrue($ret[$i++] === FALSE); // lremove
        
$this->assertTrue($ret[$i++] === FALSE); // lpop
        
$this->assertTrue($ret[$i++] === FALSE); // rpop
        
$this->assertTrue($ret[$i++] === FALSE); // rpoplush

        
$this->assertTrue($ret[$i++] === FALSE); // sadd
        
$this->assertTrue($ret[$i++] === FALSE); // sremove
        
$this->assertTrue($ret[$i++] === FALSE); // spop
        
$this->assertTrue($ret[$i++] === FALSE); // smove
        
$this->assertTrue($ret[$i++] === FALSE); // scard
        
$this->assertTrue($ret[$i++] === FALSE); // sismember
        
$this->assertTrue($ret[$i++] === FALSE); // sinter
        
$this->assertTrue($ret[$i++] === FALSE); // sunion
        
$this->assertTrue($ret[$i++] === FALSE); // sdiff
        
$this->assertTrue($ret[$i++] === FALSE); // smembers
        
$this->assertTrue($ret[$i++] === FALSE); // srandmember

        
$this->assertTrue($ret[$i++] === FALSE); // zadd
        
$this->assertTrue($ret[$i++] === FALSE); // zrem
        
$this->assertTrue($ret[$i++] === FALSE); // zincrby
        
$this->assertTrue($ret[$i++] === FALSE); // zrank
        
$this->assertTrue($ret[$i++] === FALSE); // zrevrank
        
$this->assertTrue($ret[$i++] === FALSE); // zrange
        
$this->assertTrue($ret[$i++] === FALSE); // zreverserange
        
$this->assertTrue($ret[$i++] === FALSE); // zrangebyscore
        
$this->assertTrue($ret[$i++] === FALSE); // zcount
        
$this->assertTrue($ret[$i++] === FALSE); // zcard
        
$this->assertTrue($ret[$i++] === FALSE); // zscore
        
$this->assertTrue($ret[$i++] === FALSE); // zremrangebyrank
        
$this->assertTrue($ret[$i++] === FALSE); // zremrangebyscore

        
$this->assertTrue($ret[$i++] === FALSE); // hset
        
$this->assertTrue($ret[$i++] === FALSE); // hget
        
$this->assertTrue($ret[$i++] === FALSE); // hmget
        
$this->assertTrue($ret[$i++] === FALSE); // hmset
        
$this->assertTrue($ret[$i++] === FALSE); // hincrby
        
$this->assertTrue($ret[$i++] === FALSE); // hexists
        
$this->assertTrue($ret[$i++] === FALSE); // hdel
        
$this->assertTrue($ret[$i++] === FALSE); // hlen
        
$this->assertTrue($ret[$i++] === FALSE); // hkeys
        
$this->assertTrue($ret[$i++] === FALSE); // hvals
        
$this->assertTrue($ret[$i++] === FALSE); // hgetall

        
$this->assertEquals($icount($ret));

        
// list
        
$key '{hash}list';
        
$dkey '{hash}' __FUNCTION__;
        
$ret $this->redis->multi($mode)
            ->
del($key)
            ->
lpush($key'lvalue')

            
// string I/F
            
->get($key)
            ->
getset($key'value2')
            ->
append($key'append')
            ->
getRange($key08)
            ->
mget([$key])
            ->
incr($key)
            ->
incrBy($key1)
            ->
decr($key)
            ->
decrBy($key1)

            
// sets I/F
            
->sAdd($key'sValue1')
            ->
srem($key'sValue1')
            ->
sPop($key)
            ->
sMove($key$dkey 'skey1''sValue1')
            ->
scard($key)
            ->
sismember($key'sValue1')
            ->
sInter($key$dkey 'skey2')
            ->
sUnion($key$dkey 'skey4')
            ->
sDiff($key$dkey 'skey7')
            ->
sMembers($key)
            ->
sRandMember($key)

            
// sorted sets I/F
            
->zAdd($key1'zValue1')
            ->
zRem($key'zValue1')
            ->
zIncrBy($key1'zValue1')
            ->
zRank($key'zValue1')
            ->
zRevRank($key'zValue1')
            ->
zRange($key0, -1)
            ->
zRevRange($key0, -1)
            ->
zRangeByScore($key12)
            ->
zCount($key0, -1)
            ->
zCard($key)
            ->
zScore($key'zValue1')
            ->
zRemRangeByRank($key12)
            ->
zRemRangeByScore($key12)

            
// hash I/F
            
->hSet($key'key1''value1')
            ->
hGet($key'key1')
            ->
hMGet($key, ['key1'])
            ->
hMSet($key, ['key1' => 'value1'])
            ->
hIncrBy($key'key2'1)
            ->
hExists($key'key2')
            ->
hDel($key'key2')
            ->
hLen($key)
            ->
hKeys($key)
            ->
hVals($key)
            ->
hGetAll($key)

            ->
exec();

        
$i 0;
        
$this->assertTrue(is_array($ret));
        
$this->assertTrue(is_long($ret[$i++])); // delete
        
$this->assertTrue($ret[$i++] === 1); // lpush

        
$this->assertTrue($ret[$i++] === FALSE); // get
        
$this->assertTrue($ret[$i++] === FALSE); // getset
        
$this->assertTrue($ret[$i++] === FALSE); // append
        
$this->assertTrue($ret[$i++] === FALSE); // getRange
        
$this->assertTrue(is_array($ret[$i]) && count($ret[$i]) === && $ret[$i][0] === FALSE); // mget
        
$i++;
        
$this->assertTrue($ret[$i++] === FALSE); // incr
        
$this->assertTrue($ret[$i++] === FALSE); // incrBy
        
$this->assertTrue($ret[$i++] === FALSE); // decr
        
$this->assertTrue($ret[$i++] === FALSE); // decrBy

        
$this->assertTrue($ret[$i++] === FALSE); // sadd
        
$this->assertTrue($ret[$i++] === FALSE); // sremove
        
$this->assertTrue($ret[$i++] === FALSE); // spop
        
$this->assertTrue($ret[$i++] === FALSE); // smove
        
$this->assertTrue($ret[$i++] === FALSE); // scard
        
$this->assertTrue($ret[$i++] === FALSE); // sismember
        
$this->assertTrue($ret[$i++] === FALSE); // sinter
        
$this->assertTrue($ret[$i++] === FALSE); // sunion
        
$this->assertTrue($ret[$i++] === FALSE); // sdiff
        
$this->assertTrue($ret[$i++] === FALSE); // smembers
        
$this->assertTrue($ret[$i++] === FALSE); // srandmember

        
$this->assertTrue($ret[$i++] === FALSE); // zadd
        
$this->assertTrue($ret[$i++] === FALSE); // zrem
        
$this->assertTrue($ret[$i++] === FALSE); // zincrby
        
$this->assertTrue($ret[$i++] === FALSE); // zrank
        
$this->assertTrue($ret[$i++] === FALSE); // zrevrank
        
$this->assertTrue($ret[$i++] === FALSE); // zrange
        
$this->assertTrue($ret[$i++] === FALSE); // zreverserange
        
$this->assertTrue($ret[$i++] === FALSE); // zrangebyscore
        
$this->assertTrue($ret[$i++] === FALSE); // zcount
        
$this->assertTrue($ret[$i++] === FALSE); // zcard
        
$this->assertTrue($ret[$i++] === FALSE); // zscore
        
$this->assertTrue($ret[$i++] === FALSE); // zremrangebyrank
        
$this->assertTrue($ret[$i++] === FALSE); // zremrangebyscore

        
$this->assertTrue($ret[$i++] === FALSE); // hset
        
$this->assertTrue($ret[$i++] === FALSE); // hget
        
$this->assertTrue($ret[$i++] === FALSE); // hmget
        
$this->assertTrue($ret[$i++] === FALSE); // hmset
        
$this->assertTrue($ret[$i++] === FALSE); // hincrby
        
$this->assertTrue($ret[$i++] === FALSE); // hexists
        
$this->assertTrue($ret[$i++] === FALSE); // hdel
        
$this->assertTrue($ret[$i++] === FALSE); // hlen
        
$this->assertTrue($ret[$i++] === FALSE); // hkeys
        
$this->assertTrue($ret[$i++] === FALSE); // hvals
        
$this->assertTrue($ret[$i++] === FALSE); // hgetall

        
$this->assertEquals($icount($ret));

        
// set
        
$key '{hash}set';
        
$dkey '{hash}' __FUNCTION__;
        
$ret $this->redis->multi($mode)
            ->
del($key)
            ->
sAdd($key'sValue')

            
// string I/F
            
->get($key)
            ->
getset($key'value2')
            ->
append($key'append')
            ->
getRange($key08)
            ->
mget([$key])
            ->
incr($key)
            ->
incrBy($key1)
            ->
decr($key)
            ->
decrBy($key1)

            
// lists I/F
            
->rPush($key'lvalue')
            ->
lPush($key'lvalue')
            ->
lLen($key)
            ->
lPop($key)
            ->
lrange($key0, -1)
            ->
lTrim($key01)
            ->
lIndex($key0)
            ->
lSet($key0"newValue")
            ->
lrem($key'lvalue'1)
            ->
lPop($key)
            ->
rPop($key)
            ->
rPoplPush($key$dkey 'lkey1')

            
// sorted sets I/F
            
->zAdd($key1'zValue1')
            ->
zRem($key'zValue1')
            ->
zIncrBy($key1'zValue1')
            ->
zRank($key'zValue1')
            ->
zRevRank($key'zValue1')
            ->
zRange($key0, -1)
            ->
zRevRange($key0, -1)
            ->
zRangeByScore($key12)
            ->
zCount($key0, -1)
            ->
zCard($key)
            ->
zScore($key'zValue1')
            ->
zRemRangeByRank($key12)
            ->
zRemRangeByScore($key12)

            
// hash I/F
            
->hSet($key'key1''value1')
            ->
hGet($key'key1')
            ->
hMGet($key, ['key1'])
            ->
hMSet($key, ['key1' => 'value1'])
            ->
hIncrBy($key'key2'1)
            ->
hExists($key'key2')
            ->
hDel($key'key2')
            ->
hLen($key)
            ->
hKeys($key)
            ->
hVals($key)
            ->
hGetAll($key)

            ->
exec();

        
$i 0;
        
$this->assertTrue(is_array($ret));
        
$this->assertTrue(is_long($ret[$i++])); // delete
        
$this->assertTrue($ret[$i++] === 1); // zadd

        
$this->assertTrue($ret[$i++] === FALSE); // get
        
$this->assertTrue($ret[$i++] === FALSE); // getset
        
$this->assertTrue($ret[$i++] === FALSE); // append
        
$this->assertTrue($ret[$i++] === FALSE); // getRange
        
$this->assertTrue(is_array($ret[$i]) && count($ret[$i]) === && $ret[$i][0] === FALSE); // mget
        
$i++;
        
$this->assertTrue($ret[$i++] === FALSE); // incr
        
$this->assertTrue($ret[$i++] === FALSE); // incrBy
        
$this->assertTrue($ret[$i++] === FALSE); // decr
        
$this->assertTrue($ret[$i++] === FALSE); // decrBy

        
$this->assertTrue($ret[$i++] === FALSE); // rpush
        
$this->assertTrue($ret[$i++] === FALSE); // lpush
        
$this->assertTrue($ret[$i++] === FALSE); // llen
        
$this->assertTrue($ret[$i++] === FALSE); // lpop
        
$this->assertTrue($ret[$i++] === FALSE); // lrange
        
$this->assertTrue($ret[$i++] === FALSE); // ltrim
        
$this->assertTrue($ret[$i++] === FALSE); // lindex
        
$this->assertTrue($ret[$i++] === FALSE); // lset
        
$this->assertTrue($ret[$i++] === FALSE); // lremove
        
$this->assertTrue($ret[$i++] === FALSE); // lpop
        
$this->assertTrue($ret[$i++] === FALSE); // rpop
        
$this->assertTrue($ret[$i++] === FALSE); // rpoplush

        
$this->assertTrue($ret[$i++] === FALSE); // zadd
        
$this->assertTrue($ret[$i++] === FALSE); // zrem
        
$this->assertTrue($ret[$i++] === FALSE); // zincrby
        
$this->assertTrue($ret[$i++] === FALSE); // zrank
        
$this->assertTrue($ret[$i++] === FALSE); // zrevrank
        
$this->assertTrue($ret[$i++] === FALSE); // zrange
        
$this->assertTrue($ret[$i++] === FALSE); // zreverserange
        
$this->assertTrue($ret[$i++] === FALSE); // zrangebyscore
        
$this->assertTrue($ret[$i++] === FALSE); // zcount
        
$this->assertTrue($ret[$i++] === FALSE); // zcard
        
$this->assertTrue($ret[$i++] === FALSE); // zscore
        
$this->assertTrue($ret[$i++] === FALSE); // zremrangebyrank
        
$this->assertTrue($ret[$i++] === FALSE); // zremrangebyscore

        
$this->assertTrue($ret[$i++] === FALSE); // hset
        
$this->assertTrue($ret[$i++] === FALSE); // hget
        
$this->assertTrue($ret[$i++] === FALSE); // hmget
        
$this->assertTrue($ret[$i++] === FALSE); // hmset
        
$this->assertTrue($ret[$i++] === FALSE); // hincrby
        
$this->assertTrue($ret[$i++] === FALSE); // hexists
        
$this->assertTrue($ret[$i++] === FALSE); // hdel
        
$this->assertTrue($ret[$i++] === FALSE); // hlen
        
$this->assertTrue($ret[$i++] === FALSE); // hkeys
        
$this->assertTrue($ret[$i++] === FALSE); // hvals
        
$this->assertTrue($ret[$i++] === FALSE); // hgetall

        
$this->assertEquals($icount($ret));

        
// sorted set
        
$key '{hash}sortedset';
        
$dkey '{hash}' __FUNCTION__;
        
$ret $this->redis->multi($mode)
            ->
del($key)
            ->
zAdd($key0'zValue')

            
// string I/F
            
->get($key)
            ->
getset($key'value2')
            ->
append($key'append')
            ->
getRange($key08)
            ->
mget([$key])
            ->
incr($key)
            ->
incrBy($key1)
            ->
decr($key)
            ->
decrBy($key1)

            
// lists I/F
            
->rPush($key'lvalue')
            ->
lPush($key'lvalue')
            ->
lLen($key)
            ->
lPop($key)
            ->
lrange($key0, -1)
            ->
lTrim($key01)
            ->
lIndex($key0)
            ->
lSet($key0"newValue")
            ->
lrem($key'lvalue'1)
            ->
lPop($key)
            ->
rPop($key)
            ->
rPoplPush($key$dkey 'lkey1')

            
// sets I/F
            
->sAdd($key'sValue1')
            ->
srem($key'sValue1')
            ->
sPop($key)
            ->
sMove($key$dkey 'skey1''sValue1')
            ->
scard($key)
            ->
sismember($key'sValue1')
            ->
sInter($key$dkey 'skey2')
            ->
sUnion($key$dkey 'skey4')
            ->
sDiff($key$dkey 'skey7')
            ->
sMembers($key)
            ->
sRandMember($key)

            
// hash I/F
            
->hSet($key'key1''value1')
            ->
hGet($key'key1')
            ->
hMGet($key, ['key1'])
            ->
hMSet($key, ['key1' => 'value1'])
            ->
hIncrBy($key'key2'1)
            ->
hExists($key'key2')
            ->
hDel($key'key2')
            ->
hLen($key)
            ->
hKeys($key)
            ->
hVals($key)
            ->
hGetAll($key)

            ->
exec();

        
$i 0;
        
$this->assertTrue(is_array($ret));
        
$this->assertTrue(is_long($ret[$i++])); // delete
        
$this->assertTrue($ret[$i++] === 1); // zadd

        
$this->assertTrue($ret[$i++] === FALSE); // get
        
$this->assertTrue($ret[$i++] === FALSE); // getset
        
$this->assertTrue($ret[$i++] === FALSE); // append
        
$this->assertTrue($ret[$i++] === FALSE); // getRange
        
$this->assertTrue(is_array($ret[$i]) && count($ret[$i]) === && $ret[$i][0] === FALSE); // mget
        
$i++;
        
$this->assertTrue($ret[$i++] === FALSE); // incr
        
$this->assertTrue($ret[$i++] === FALSE); // incrBy
        
$this->assertTrue($ret[$i++] === FALSE); // decr
        
$this->assertTrue($ret[$i++] === FALSE); // decrBy

        
$this->assertTrue($ret[$i++] === FALSE); // rpush
        
$this->assertTrue($ret[$i++] === FALSE); // lpush
        
$this->assertTrue($ret[$i++] === FALSE); // llen
        
$this->assertTrue($ret[$i++] === FALSE); // lpop
        
$this->assertTrue($ret[$i++] === FALSE); // lrange
        
$this->assertTrue($ret[$i++] === FALSE); // ltrim
        
$this->assertTrue($ret[$i++] === FALSE); // lindex
        
$this->assertTrue($ret[$i++] === FALSE); // lset
        
$this->assertTrue($ret[$i++] === FALSE); // lremove
        
$this->assertTrue($ret[$i++] === FALSE); // lpop
        
$this->assertTrue($ret[$i++] === FALSE); // rpop
        
$this->assertTrue($ret[$i++] === FALSE); // rpoplush

        
$this->assertTrue($ret[$i++] === FALSE); // sadd
        
$this->assertTrue($ret[$i++] === FALSE); // sremove
        
$this->assertTrue($ret[$i++] === FALSE); // spop
        
$this->assertTrue($ret[$i++] === FALSE); // smove
        
$this->assertTrue($ret[$i++] === FALSE); // scard
        
$this->assertTrue($ret[$i++] === FALSE); // sismember
        
$this->assertTrue($ret[$i++] === FALSE); // sinter
        
$this->assertTrue($ret[$i++] === FALSE); // sunion
        
$this->assertTrue($ret[$i++] === FALSE); // sdiff
        
$this->assertTrue($ret[$i++] === FALSE); // smembers
        
$this->assertTrue($ret[$i++] === FALSE); // srandmember

        
$this->assertTrue($ret[$i++] === FALSE); // hset
        
$this->assertTrue($ret[$i++] === FALSE); // hget
        
$this->assertTrue($ret[$i++] === FALSE); // hmget
        
$this->assertTrue($ret[$i++] === FALSE); // hmset
        
$this->assertTrue($ret[$i++] === FALSE); // hincrby
        
$this->assertTrue($ret[$i++] === FALSE); // hexists
        
$this->assertTrue($ret[$i++] === FALSE); // hdel
        
$this->assertTrue($ret[$i++] === FALSE); // hlen
        
$this->assertTrue($ret[$i++] === FALSE); // hkeys
        
$this->assertTrue($ret[$i++] === FALSE); // hvals
        
$this->assertTrue($ret[$i++] === FALSE); // hgetall

        
$this->assertEquals($icount($ret));

        
// hash
        
$key '{hash}hash';
        
$dkey '{hash}' __FUNCTION__;
        
$ret $this->redis->multi($mode)
            ->
del($key)
            ->
hset($key'key1''hValue')

            
// string I/F
            
->get($key)
            ->
getset($key'value2')
            ->
append($key'append')
            ->
getRange($key08)
            ->
mget([$key])
            ->
incr($key)
            ->
incrBy($key1)
            ->
decr($key)
            ->
decrBy($key1)

            
// lists I/F
            
->rPush($key'lvalue')
            ->
lPush($key'lvalue')
            ->
lLen($key)
            ->
lPop($key)
            ->
lrange($key0, -1)
            ->
lTrim($key01)
            ->
lIndex($key0)
            ->
lSet($key0"newValue")
            ->
lrem($key'lvalue'1)
            ->
lPop($key)
            ->
rPop($key)
            ->
rPoplPush($key$dkey 'lkey1')

            
// sets I/F
            
->sAdd($key'sValue1')
            ->
srem($key'sValue1')
            ->
sPop($key)
            ->
sMove($key$dkey 'skey1''sValue1')
            ->
scard($key)
            ->
sismember($key'sValue1')
            ->
sInter($key$dkey 'skey2')
            ->
sUnion($key$dkey 'skey4')
            ->
sDiff($key$dkey 'skey7')
            ->
sMembers($key)
            ->
sRandMember($key)

            
// sorted sets I/F
            
->zAdd($key1'zValue1')
            ->
zRem($key'zValue1')
            ->
zIncrBy($key1'zValue1')
            ->
zRank($key'zValue1')
            ->
zRevRank($key'zValue1')
            ->
zRange($key0, -1)
            ->
zRevRange($key0, -1)
            ->
zRangeByScore($key12)
            ->
zCount($key0, -1)
            ->
zCard($key)
            ->
zScore($key'zValue1')
            ->
zRemRangeByRank($key12)
            ->
zRemRangeByScore($key12)

            ->
exec();

        
$i 0;
        
$this->assertTrue(is_array($ret));
        
$this->assertTrue(is_long($ret[$i++])); // delete
        
$this->assertTrue($ret[$i++] === 1); // hset

        
$this->assertTrue($ret[$i++] === FALSE); // get
        
$this->assertTrue($ret[$i++] === FALSE); // getset
        
$this->assertTrue($ret[$i++] === FALSE); // append
        
$this->assertTrue($ret[$i++] === FALSE); // getRange
        
$this->assertTrue(is_array($ret[$i]) && count($ret[$i]) === && $ret[$i][0] === FALSE); // mget
        
$i++;
        
$this->assertTrue($ret[$i++] === FALSE); // incr
        
$this->assertTrue($ret[$i++] === FALSE); // incrBy
        
$this->assertTrue($ret[$i++] === FALSE); // decr
        
$this->assertTrue($ret[$i++] === FALSE); // decrBy

        
$this->assertTrue($ret[$i++] === FALSE); // rpush
        
$this->assertTrue($ret[$i++] === FALSE); // lpush
        
$this->assertTrue($ret[$i++] === FALSE); // llen
        
$this->assertTrue($ret[$i++] === FALSE); // lpop
        
$this->assertTrue($ret[$i++] === FALSE); // lrange
        
$this->assertTrue($ret[$i++] === FALSE); // ltrim
        
$this->assertTrue($ret[$i++] === FALSE); // lindex
        
$this->assertTrue($ret[$i++] === FALSE); // lset
        
$this->assertTrue($ret[$i++] === FALSE); // lremove
        
$this->assertTrue($ret[$i++] === FALSE); // lpop
        
$this->assertTrue($ret[$i++] === FALSE); // rpop
        
$this->assertTrue($ret[$i++] === FALSE); // rpoplush

        
$this->assertTrue($ret[$i++] === FALSE); // sadd
        
$this->assertTrue($ret[$i++] === FALSE); // sremove
        
$this->assertTrue($ret[$i++] === FALSE); // spop
        
$this->assertTrue($ret[$i++] === FALSE); // smove
        
$this->assertTrue($ret[$i++] === FALSE); // scard
        
$this->assertTrue($ret[$i++] === FALSE); // sismember
        
$this->assertTrue($ret[$i++] === FALSE); // sinter
        
$this->assertTrue($ret[$i++] === FALSE); // sunion
        
$this->assertTrue($ret[$i++] === FALSE); // sdiff
        
$this->assertTrue($ret[$i++] === FALSE); // smembers
        
$this->assertTrue($ret[$i++] === FALSE); // srandmember

        
$this->assertTrue($ret[$i++] === FALSE); // zadd
        
$this->assertTrue($ret[$i++] === FALSE); // zrem
        
$this->assertTrue($ret[$i++] === FALSE); // zincrby
        
$this->assertTrue($ret[$i++] === FALSE); // zrank
        
$this->assertTrue($ret[$i++] === FALSE); // zrevrank
        
$this->assertTrue($ret[$i++] === FALSE); // zrange
        
$this->assertTrue($ret[$i++] === FALSE); // zreverserange
        
$this->assertTrue($ret[$i++] === FALSE); // zrangebyscore
        
$this->assertTrue($ret[$i++] === FALSE); // zcount
        
$this->assertTrue($ret[$i++] === FALSE); // zcard
        
$this->assertTrue($ret[$i++] === FALSE); // zscore
        
$this->assertTrue($ret[$i++] === FALSE); // zremrangebyrank
        
$this->assertTrue($ret[$i++] === FALSE); // zremrangebyscore

        
$this->assertEquals($icount($ret));
    }

    public function 
testDifferentTypeString() {
        
$key '{hash}string';
        
$dkey '{hash}' __FUNCTION__;

        
$this->redis->del($key);
        
$this->assertEquals(TRUE$this->redis->set($key'value'));

        
// lists I/F
        
$this->assertEquals(FALSE$this->redis->rPush($key'lvalue'));
        
$this->assertEquals(FALSE$this->redis->lPush($key'lvalue'));
        
$this->assertEquals(FALSE$this->redis->lLen($key));
        
$this->assertEquals(FALSE$this->redis->lPop($key));
        
$this->assertEquals(FALSE$this->redis->lrange($key0, -1));
        
$this->assertEquals(FALSE$this->redis->lTrim($key01));
        
$this->assertEquals(FALSE$this->redis->lIndex($key0));
        
$this->assertEquals(FALSE$this->redis->lSet($key0"newValue"));
        
$this->assertEquals(FALSE$this->redis->lrem($key'lvalue'1));
        
$this->assertEquals(FALSE$this->redis->lPop($key));
        
$this->assertEquals(FALSE$this->redis->rPop($key));
        
$this->assertEquals(FALSE$this->redis->rPoplPush($key$dkey 'lkey1'));

        
// sets I/F
        
$this->assertEquals(FALSE$this->redis->sAdd($key'sValue1'));
        
$this->assertEquals(FALSE$this->redis->srem($key'sValue1'));
        
$this->assertEquals(FALSE$this->redis->sPop($key));
        
$this->assertEquals(FALSE$this->redis->sMove($key$dkey 'skey1''sValue1'));
        
$this->assertEquals(FALSE$this->redis->scard($key));
        
$this->assertEquals(FALSE$this->redis->sismember($key'sValue1'));
        
$this->assertEquals(FALSE$this->redis->sInter($key$dkey'skey2'));
        
$this->assertEquals(FALSE$this->redis->sUnion($key$dkey 'skey4'));
        
$this->assertEquals(FALSE$this->redis->sDiff($key$dkey 'skey7'));
        
$this->assertEquals(FALSE$this->redis->sMembers($key));
        
$this->assertEquals(FALSE$this->redis->sRandMember($key));

        
// sorted sets I/F
        
$this->assertEquals(FALSE$this->redis->zAdd($key1'zValue1'));
        
$this->assertEquals(FALSE$this->redis->zRem($key'zValue1'));
        
$this->assertEquals(FALSE$this->redis->zIncrBy($key1'zValue1'));
        
$this->assertEquals(FALSE$this->redis->zRank($key'zValue1'));
        
$this->assertEquals(FALSE$this->redis->zRevRank($key'zValue1'));
        
$this->assertEquals(FALSE$this->redis->zRange($key0, -1));
        
$this->assertEquals(FALSE$this->redis->zRevRange($key0, -1));
        
$this->assertEquals(FALSE$this->redis->zRangeByScore($key12));
        
$this->assertEquals(FALSE$this->redis->zCount($key0, -1));
        
$this->assertEquals(FALSE$this->redis->zCard($key));
        
$this->assertEquals(FALSE$this->redis->zScore($key'zValue1'));
        
$this->assertEquals(FALSE$this->redis->zRemRangeByRank($key12));
        
$this->assertEquals(FALSE$this->redis->zRemRangeByScore($key12));

        
// hash I/F
        
$this->assertEquals(FALSE$this->redis->hSet($key'key1''value1'));
        
$this->assertEquals(FALSE$this->redis->hGet($key'key1'));
        
$this->assertEquals(FALSE$this->redis->hMGet($key, ['key1']));
        
$this->assertEquals(FALSE$this->redis->hMSet($key, ['key1' => 'value1']));
        
$this->assertEquals(FALSE$this->redis->hIncrBy($key'key2'1));
        
$this->assertEquals(FALSE$this->redis->hExists($key'key2'));
        
$this->assertEquals(FALSE$this->redis->hDel($key'key2'));
        
$this->assertEquals(FALSE$this->redis->hLen($key));
        
$this->assertEquals(FALSE$this->redis->hKeys($key));
        
$this->assertEquals(FALSE$this->redis->hVals($key));
        
$this->assertEquals(FALSE$this->redis->hGetAll($key));
    }

    public function 
testDifferentTypeList() {
        
$key '{hash}list';
        
$dkey '{hash}' __FUNCTION__;

        
$this->redis->del($key);
        
$this->assertEquals(1$this->redis->lPush($key'value'));

        
// string I/F
        
$this->assertEquals(FALSE$this->redis->get($key));
        
$this->assertEquals(FALSE$this->redis->getset($key'value2'));
        
$this->assertEquals(FALSE$this->redis->append($key'append'));
        
$this->assertEquals(FALSE$this->redis->getRange($key08));
        
$this->assertEquals([FALSE], $this->redis->mget([$key]));
        
$this->assertEquals(FALSE$this->redis->incr($key));
        
$this->assertEquals(FALSE$this->redis->incrBy($key1));
        
$this->assertEquals(FALSE$this->redis->decr($key));
        
$this->assertEquals(FALSE$this->redis->decrBy($key1));

        
// sets I/F
        
$this->assertEquals(FALSE$this->redis->sAdd($key'sValue1'));
        
$this->assertEquals(FALSE$this->redis->srem($key'sValue1'));
        
$this->assertEquals(FALSE$this->redis->sPop($key));
        
$this->assertEquals(FALSE$this->redis->sMove($key$dkey 'skey1''sValue1'));
        
$this->assertEquals(FALSE$this->redis->scard($key));
        
$this->assertEquals(FALSE$this->redis->sismember($key'sValue1'));
        
$this->assertEquals(FALSE$this->redis->sInter($key$dkey 'skey2'));
        
$this->assertEquals(FALSE$this->redis->sUnion($key$dkey 'skey4'));
        
$this->assertEquals(FALSE$this->redis->sDiff($key$dkey 'skey7'));
        
$this->assertEquals(FALSE$this->redis->sMembers($key));
        
$this->assertEquals(FALSE$this->redis->sRandMember($key));

        
// sorted sets I/F
        
$this->assertEquals(FALSE$this->redis->zAdd($key1'zValue1'));
        
$this->assertEquals(FALSE$this->redis->zRem($key'zValue1'));
        
$this->assertEquals(FALSE$this->redis->zIncrBy($key1'zValue1'));
        
$this->assertEquals(FALSE$this->redis->zRank($key'zValue1'));
        
$this->assertEquals(FALSE$this->redis->zRevRank($key'zValue1'));
        
$this->assertEquals(FALSE$this->redis->zRange($key0, -1));
        
$this->assertEquals(FALSE$this->redis->zRevRange($key0, -1));
        
$this->assertEquals(FALSE$this->redis->zRangeByScore($key12));
        
$this->assertEquals(FALSE$this->redis->zCount($key0, -1));
        
$this->assertEquals(FALSE$this->redis->zCard($key));
        
$this->assertEquals(FALSE$this->redis->zScore($key'zValue1'));
        
$this->assertEquals(FALSE$this->redis->zRemRangeByRank($key12));
        
$this->assertEquals(FALSE$this->redis->zRemRangeByScore($key12));

        
// hash I/F
        
$this->assertEquals(FALSE$this->redis->hSet($key'key1''value1'));
        
$this->assertEquals(FALSE$this->redis->hGet($key'key1'));
        
$this->assertEquals(FALSE$this->redis->hMGet($key, ['key1']));
        
$this->assertEquals(FALSE$this->redis->hMSet($key, ['key1' => 'value1']));
        
$this->assertEquals(FALSE$this->redis->hIncrBy($key'key2'1));
        
$this->assertEquals(FALSE$this->redis->hExists($key'key2'));
        
$this->assertEquals(FALSE$this->redis->hDel($key'key2'));
        
$this->assertEquals(FALSE$this->redis->hLen($key));
        
$this->assertEquals(FALSE$this->redis->hKeys($key));
        
$this->assertEquals(FALSE$this->redis->hVals($key));
        
$this->assertEquals(FALSE$this->redis->hGetAll($key));
    }

    public function 
testDifferentTypeSet() {
        
$key '{hash}set';
        
$dkey '{hash}' __FUNCTION__;
        
$this->redis->del($key);
        
$this->assertEquals(1$this->redis->sAdd($key'value'));

        
// string I/F
        
$this->assertEquals(FALSE$this->redis->get($key));
        
$this->assertEquals(FALSE$this->redis->getset($key'value2'));
        
$this->assertEquals(FALSE$this->redis->append($key'append'));
        
$this->assertEquals(FALSE$this->redis->getRange($key08));
        
$this->assertEquals([FALSE], $this->redis->mget([$key]));
        
$this->assertEquals(FALSE$this->redis->incr($key));
        
$this->assertEquals(FALSE$this->redis->incrBy($key1));
        
$this->assertEquals(FALSE$this->redis->decr($key));
        
$this->assertEquals(FALSE$this->redis->decrBy($key1));

        
// lists I/F
        
$this->assertEquals(FALSE$this->redis->rPush($key'lvalue'));
        
$this->assertEquals(FALSE$this->redis->lPush($key'lvalue'));
        
$this->assertEquals(FALSE$this->redis->lLen($key));
        
$this->assertEquals(FALSE$this->redis->lPop($key));
        
$this->assertEquals(FALSE$this->redis->lrange($key0, -1));
        
$this->assertEquals(FALSE$this->redis->lTrim($key01));
        
$this->assertEquals(FALSE$this->redis->lIndex($key0));
        
$this->assertEquals(FALSE$this->redis->lSet($key0"newValue"));
        
$this->assertEquals(FALSE$this->redis->lrem($key'lvalue'1));
        
$this->assertEquals(FALSE$this->redis->lPop($key));
        
$this->assertEquals(FALSE$this->redis->rPop($key));
        
$this->assertEquals(FALSE$this->redis->rPoplPush($key$dkey  'lkey1'));

        
// sorted sets I/F
        
$this->assertEquals(FALSE$this->redis->zAdd($key1'zValue1'));
        
$this->assertEquals(FALSE$this->redis->zRem($key'zValue1'));
        
$this->assertEquals(FALSE$this->redis->zIncrBy($key1'zValue1'));
        
$this->assertEquals(FALSE$this->redis->zRank($key'zValue1'));
        
$this->assertEquals(FALSE$this->redis->zRevRank($key'zValue1'));
        
$this->assertEquals(FALSE$this->redis->zRange($key0, -1));
        
$this->assertEquals(FALSE$this->redis->zRevRange($key0, -1));
        
$this->assertEquals(FALSE$this->redis->zRangeByScore($key12));
        
$this->assertEquals(FALSE$this->redis->zCount($key0, -1));
        
$this->assertEquals(FALSE$this->redis->zCard($key));
        
$this->assertEquals(FALSE$this->redis->zScore($key'zValue1'));
        
$this->assertEquals(FALSE$this->redis->zRemRangeByRank($key12));
        
$this->assertEquals(FALSE$this->redis->zRemRangeByScore($key12));

        
// hash I/F
        
$this->assertEquals(FALSE$this->redis->hSet($key'key1''value1'));
        
$this->assertEquals(FALSE$this->redis->hGet($key'key1'));
        
$this->assertEquals(FALSE$this->redis->hMGet($key, ['key1']));
        
$this->assertEquals(FALSE$this->redis->hMSet($key, ['key1' => 'value1']));
        
$this->assertEquals(FALSE$this->redis->hIncrBy($key'key2'1));
        
$this->assertEquals(FALSE$this->redis->hExists($key'key2'));
        
$this->assertEquals(FALSE$this->redis->hDel($key'key2'));
        
$this->assertEquals(FALSE$this->redis->hLen($key));
        
$this->assertEquals(FALSE$this->redis->hKeys($key));
        
$this->assertEquals(FALSE$this->redis->hVals($key));
        
$this->assertEquals(FALSE$this->redis->hGetAll($key));
    }

    public function 
testDifferentTypeSortedSet() {
        
$key '{hash}sortedset';
        
$dkey '{hash}' __FUNCTION__;

        
$this->redis->del($key);
        
$this->assertEquals(1$this->redis->zAdd($key0'value'));

        
// string I/F
        
$this->assertEquals(FALSE$this->redis->get($key));
        
$this->assertEquals(FALSE$this->redis->getset($key'value2'));
        
$this->assertEquals(FALSE$this->redis->append($key'append'));
        
$this->assertEquals(FALSE$this->redis->getRange($key08));
        
$this->assertEquals([FALSE], $this->redis->mget([$key]));
        
$this->assertEquals(FALSE$this->redis->incr($key));
        
$this->assertEquals(FALSE$this->redis->incrBy($key1));
        
$this->assertEquals(FALSE$this->redis->decr($key));
        
$this->assertEquals(FALSE$this->redis->decrBy($key1));

        
// lists I/F
        
$this->assertEquals(FALSE$this->redis->rPush($key'lvalue'));
        
$this->assertEquals(FALSE$this->redis->lPush($key'lvalue'));
        
$this->assertEquals(FALSE$this->redis->lLen($key));
        
$this->assertEquals(FALSE$this->redis->lPop($key));
        
$this->assertEquals(FALSE$this->redis->lrange($key0, -1));
        
$this->assertEquals(FALSE$this->redis->lTrim($key01));
        
$this->assertEquals(FALSE$this->redis->lIndex($key0));
        
$this->assertEquals(FALSE$this->redis->lSet($key0"newValue"));
        
$this->assertEquals(FALSE$this->redis->lrem($key'lvalue'1));
        
$this->assertEquals(FALSE$this->redis->lPop($key));
        
$this->assertEquals(FALSE$this->redis->rPop($key));
        
$this->assertEquals(FALSE$this->redis->rPoplPush($key$dkey 'lkey1'));

        
// sets I/F
        
$this->assertEquals(FALSE$this->redis->sAdd($key'sValue1'));
        
$this->assertEquals(FALSE$this->redis->srem($key'sValue1'));
        
$this->assertEquals(FALSE$this->redis->sPop($key));
        
$this->assertEquals(FALSE$this->redis->sMove($key$dkey 'skey1''sValue1'));
        
$this->assertEquals(FALSE$this->redis->scard($key));
        
$this->assertEquals(FALSE$this->redis->sismember($key'sValue1'));
        
$this->assertEquals(FALSE$this->redis->sInter($key$dkey 'skey2'));
        
$this->assertEquals(FALSE$this->redis->sUnion($key$dkey 'skey4'));
        
$this->assertEquals(FALSE$this->redis->sDiff($key$dkey 'skey7'));
        
$this->assertEquals(FALSE$this->redis->sMembers($key));
        
$this->assertEquals(FALSE$this->redis->sRandMember($key));

        
// hash I/F
        
$this->assertEquals(FALSE$this->redis->hSet($key'key1''value1'));
        
$this->assertEquals(FALSE$this->redis->hGet($key'key1'));
        
$this->assertEquals(FALSE$this->redis->hMGet($key, ['key1']));
        
$this->assertEquals(FALSE$this->redis->hMSet($key, ['key1' => 'value1']));
        
$this->assertEquals(FALSE$this->redis->hIncrBy($key'key2'1));
        
$this->assertEquals(FALSE$this->redis->hExists($key'key2'));
        
$this->assertEquals(FALSE$this->redis->hDel($key'key2'));
        
$this->assertEquals(FALSE$this->redis->hLen($key));
        
$this->assertEquals(FALSE$this->redis->hKeys($key));
        
$this->assertEquals(FALSE$this->redis->hVals($key));
        
$this->assertEquals(FALSE$this->redis->hGetAll($key));
    }

    public function 
testDifferentTypeHash() {
        
$key '{hash}hash';
        
$dkey '{hash}hash';

        
$this->redis->del($key);
        
$this->assertEquals(1$this->redis->hSet($key'key''value'));

        
// string I/F
        
$this->assertEquals(FALSE$this->redis->get($key));
        
$this->assertEquals(FALSE$this->redis->getset($key'value2'));
        
$this->assertEquals(FALSE$this->redis->append($key'append'));
        
$this->assertEquals(FALSE$this->redis->getRange($key08));
        
$this->assertEquals([FALSE], $this->redis->mget([$key]));
        
$this->assertEquals(FALSE$this->redis->incr($key));
        
$this->assertEquals(FALSE$this->redis->incrBy($key1));
        
$this->assertEquals(FALSE$this->redis->decr($key));
        
$this->assertEquals(FALSE$this->redis->decrBy($key1));

        
// lists I/F
        
$this->assertEquals(FALSE$this->redis->rPush($key'lvalue'));
        
$this->assertEquals(FALSE$this->redis->lPush($key'lvalue'));
        
$this->assertEquals(FALSE$this->redis->lLen($key));
        
$this->assertEquals(FALSE$this->redis->lPop($key));
        
$this->assertEquals(FALSE$this->redis->lrange($key0, -1));
        
$this->assertEquals(FALSE$this->redis->lTrim($key01));
        
$this->assertEquals(FALSE$this->redis->lIndex($key0));
        
$this->assertEquals(FALSE$this->redis->lSet($key0"newValue"));
        
$this->assertEquals(FALSE$this->redis->lrem($key'lvalue'1));
        
$this->assertEquals(FALSE$this->redis->lPop($key));
        
$this->assertEquals(FALSE$this->redis->rPop($key));
        
$this->assertEquals(FALSE$this->redis->rPoplPush($key$dkey 'lkey1'));

        
// sets I/F
        
$this->assertEquals(FALSE$this->redis->sAdd($key'sValue1'));
        
$this->assertEquals(FALSE$this->redis->srem($key'sValue1'));
        
$this->assertEquals(FALSE$this->redis->sPop($key));
        
$this->assertEquals(FALSE$this->redis->sMove($key$dkey 'skey1''sValue1'));
        
$this->assertEquals(FALSE$this->redis->scard($key));
        
$this->assertEquals(FALSE$this->redis->sismember($key'sValue1'));
        
$this->assertEquals(FALSE$this->redis->sInter($key$dkey 'skey2'));
        
$this->assertEquals(FALSE$this->redis->sUnion($key$dkey 'skey4'));
        
$this->assertEquals(FALSE$this->redis->sDiff($key$dkey 'skey7'));
        
$this->assertEquals(FALSE$this->redis->sMembers($key));
        
$this->assertEquals(FALSE$this->redis->sRandMember($key));

        
// sorted sets I/F
        
$this->assertEquals(FALSE$this->redis->zAdd($key1'zValue1'));
        
$this->assertEquals(FALSE$this->redis->zRem($key'zValue1'));
        
$this->assertEquals(FALSE$this->redis->zIncrBy($key1'zValue1'));
        
$this->assertEquals(FALSE$this->redis->zRank($key'zValue1'));
        
$this->assertEquals(FALSE$this->redis->zRevRank($key'zValue1'));
        
$this->assertEquals(FALSE$this->redis->zRange($key0, -1));
        
$this->assertEquals(FALSE$this->redis->zRevRange($key0, -1));
        
$this->assertEquals(FALSE$this->redis->zRangeByScore($key12));
        
$this->assertEquals(FALSE$this->redis->zCount($key0, -1));
        
$this->assertEquals(FALSE$this->redis->zCard($key));
        
$this->assertEquals(FALSE$this->redis->zScore($key'zValue1'));
        
$this->assertEquals(FALSE$this->redis->zRemRangeByRank($key12));
        
$this->assertEquals(FALSE$this->redis->zRemRangeByScore($key12));
    }

    public function 
testSerializerPHP() {
        
$this->checkSerializer(Redis::SERIALIZER_PHP);

        
// with prefix
        
$this->redis->setOption(Redis::OPT_PREFIX"test:");
        
$this->checkSerializer(Redis::SERIALIZER_PHP);
        
$this->redis->setOption(Redis::OPT_PREFIX"");
    }

    public function 
testSerializerIGBinary() {
        if(
defined('Redis::SERIALIZER_IGBINARY')) {
            
$this->checkSerializer(Redis::SERIALIZER_IGBINARY);

            
// with prefix
            
$this->redis->setOption(Redis::OPT_PREFIX"test:");
            
$this->checkSerializer(Redis::SERIALIZER_IGBINARY);
            
$this->redis->setOption(Redis::OPT_PREFIX"");
        }
    }

    public function 
testSerializerMsgPack() {
        if(
defined('Redis::SERIALIZER_MSGPACK')) {
            
$this->checkSerializer(Redis::SERIALIZER_MSGPACK);

            
// with prefix
            
$this->redis->setOption(Redis::OPT_PREFIX"test:");
            
$this->checkSerializer(Redis::SERIALIZER_MSGPACK);
            
$this->redis->setOption(Redis::OPT_PREFIX"");
        }
    }

    public function 
testSerializerJSON()
    {
        
$this->checkSerializer(Redis::SERIALIZER_JSON);

        
// with prefix
        
$this->redis->setOption(Redis::OPT_PREFIX"test:");
        
$this->checkSerializer(Redis::SERIALIZER_JSON);
        
$this->redis->setOption(Redis::OPT_PREFIX"");
    }

    private function 
checkSerializer($mode) {

        
$this->redis->del('key');
        
$this->assertTrue($this->redis->getOption(Redis::OPT_SERIALIZER) === Redis::SERIALIZER_NONE);   // default

        
$this->assertTrue($this->redis->setOption(Redis::OPT_SERIALIZER$mode) === TRUE);  // set ok
        
$this->assertTrue($this->redis->getOption(Redis::OPT_SERIALIZER) === $mode);    // get ok

        // lPush, rPush
        
$a = ['hello world'42TRUE, ['<tag>' => 1729]];
        
$this->redis->del('key');
        
$this->redis->lPush('key'$a[0]);
        
$this->redis->rPush('key'$a[1]);
        
$this->redis->rPush('key'$a[2]);
        
$this->redis->rPush('key'$a[3]);

        
// lrange
        
$this->assertTrue($a === $this->redis->lrange('key'0, -1));

        
// lIndex
        
$this->assertTrue($a[0] === $this->redis->lIndex('key'0));
        
$this->assertTrue($a[1] === $this->redis->lIndex('key'1));
        
$this->assertTrue($a[2] === $this->redis->lIndex('key'2));
        
$this->assertTrue($a[3] === $this->redis->lIndex('key'3));

        
// lrem
        
$this->assertTrue($this->redis->lrem('key'$a[3]) === 1);
        
$this->assertTrue(array_slice($a03) === $this->redis->lrange('key'0, -1));

        
// lSet
        
$a[0] = ['k' => 'v']; // update
        
$this->assertTrue(TRUE === $this->redis->lSet('key'0$a[0]));
        
$this->assertTrue($a[0] === $this->redis->lIndex('key'0));

        
// lInsert
        
$this->assertTrue($this->redis->lInsert('key'Redis::BEFORE$a[0], [1,2,3]) === 4);
        
$this->assertTrue($this->redis->lInsert('key'Redis::AFTER$a[0], [4,5,6]) === 5);

        
$a = [[1,2,3], $a[0], [4,5,6], $a[1], $a[2]];
        
$this->assertTrue($a === $this->redis->lrange('key'0, -1));

        
// sAdd
        
$this->redis->del('{set}key');
        
$s = [1,'a', [1,2,3], ['k' => 'v']];

        
$this->assertTrue(=== $this->redis->sAdd('{set}key'$s[0]));
        
$this->assertTrue(=== $this->redis->sAdd('{set}key'$s[1]));
        
$this->assertTrue(=== $this->redis->sAdd('{set}key'$s[2]));
        
$this->assertTrue(=== $this->redis->sAdd('{set}key'$s[3]));

        
// variadic sAdd
        
$this->redis->del('k');
        
$this->assertTrue(=== $this->redis->sAdd('k''a''b''c'));
        
$this->assertTrue(=== $this->redis->sAdd('k''a''b''c''d'));

        
// srem
        
$this->assertTrue(=== $this->redis->srem('{set}key'$s[3]));
        
$this->assertTrue(=== $this->redis->srem('{set}key'$s[3]));

        
// variadic
        
$this->redis->del('k');
        
$this->redis->sAdd('k''a''b''c''d');
        
$this->assertTrue(=== $this->redis->sRem('k''a''d'));
        
$this->assertTrue(=== $this->redis->sRem('k''b''c''e'));
        
$this->assertEquals(0$this->redis->exists('k'));

        
// sismember
        
$this->assertTrue(TRUE === $this->redis->sismember('{set}key'$s[0]));
        
$this->assertTrue(TRUE === $this->redis->sismember('{set}key'$s[1]));
        
$this->assertTrue(TRUE === $this->redis->sismember('{set}key'$s[2]));
        
$this->assertTrue(FALSE === $this->redis->sismember('{set}key'$s[3]));
        unset(
$s[3]);

        
// sMove
        
$this->redis->del('{set}tmp');
        
$this->redis->sMove('{set}key''{set}tmp'$s[0]);
        
$this->assertTrue(FALSE === $this->redis->sismember('{set}key'$s[0]));
        
$this->assertTrue(TRUE === $this->redis->sismember('{set}tmp'$s[0]));
        unset(
$s[0]);

        
// sorted sets
        
$z = ['z0', ['k' => 'v'], FALSENULL];
        
$this->redis->del('key');

        
// zAdd
        
$this->assertTrue(=== $this->redis->zAdd('key'0$z[0]));
        
$this->assertTrue(=== $this->redis->zAdd('key'1$z[1]));
        
$this->assertTrue(=== $this->redis->zAdd('key'2$z[2]));
        
$this->assertTrue(=== $this->redis->zAdd('key'3$z[3]));

        
// zRem
        
$this->assertTrue(=== $this->redis->zRem('key'$z[3]));
        
$this->assertTrue(=== $this->redis->zRem('key'$z[3]));
        unset(
$z[3]);

        
// check that zRem doesn't crash with a missing parameter (GitHub issue #102):
        
$this->assertTrue(FALSE === @$this->redis->zRem('key'));

        
// variadic
        
$this->redis->del('k');
        
$this->redis->zAdd('k'0'a');
        
$this->redis->zAdd('k'1'b');
        
$this->redis->zAdd('k'2'c');
        
$this->assertTrue(=== $this->redis->zRem('k''a''c'));
        
$this->assertTrue(1.0 === $this->redis->zScore('k''b'));
        
$this->assertTrue($this->redis->zRange('k'0, -1true) == ['b' => 1.0]);

        
// zRange
        
$this->assertTrue($z === $this->redis->zRange('key'0, -1));

        
// zScore
        
$this->assertTrue(0.0 === $this->redis->zScore('key'$z[0]));
        
$this->assertTrue(1.0 === $this->redis->zScore('key'$z[1]));
        
$this->assertTrue(2.0 === $this->redis->zScore('key'$z[2]));

        
// zRank
        
$this->assertTrue(=== $this->redis->zRank('key'$z[0]));
        
$this->assertTrue(=== $this->redis->zRank('key'$z[1]));
        
$this->assertTrue(=== $this->redis->zRank('key'$z[2]));

        
// zRevRank
        
$this->assertTrue(=== $this->redis->zRevRank('key'$z[0]));
        
$this->assertTrue(=== $this->redis->zRevRank('key'$z[1]));
        
$this->assertTrue(=== $this->redis->zRevRank('key'$z[2]));

        
// zIncrBy
        
$this->assertTrue(3.0 === $this->redis->zIncrBy('key'1.0$z[2]));
        
$this->assertTrue(3.0 === $this->redis->zScore('key'$z[2]));

        
$this->assertTrue(5.0 === $this->redis->zIncrBy('key'2.0$z[2]));
        
$this->assertTrue(5.0 === $this->redis->zScore('key'$z[2]));

        
$this->assertTrue(2.0 === $this->redis->zIncrBy('key', -3.0$z[2]));
        
$this->assertTrue(2.0 === $this->redis->zScore('key'$z[2]));

        
// mset
        
$a = ['k0' => 1'k1' => 42'k2' => NULL'k3' => FALSE'k4' => ['a' => 'b']];
        
$this->assertTrue(TRUE === $this->redis->mset($a));
        foreach(
$a as $k => $v) {
            
$this->assertTrue($this->redis->get($k) === $v);
        }

        
$a = ['k0' => 1'k1' => 42'k2' => NULL'k3' => FALSE'k4' => ['a' => 'b']];

        
// hSet
        
$this->redis->del('key');
        foreach(
$a as $k => $v) {
            
$this->assertTrue(=== $this->redis->hSet('key'$k$v));
        }

        
// hGet
        
foreach($a as $k => $v) {
            
$this->assertTrue($v === $this->redis->hGet('key'$k));
        }

        
// hGetAll
        
$this->assertTrue($a === $this->redis->hGetAll('key'));
        
$this->assertTrue(TRUE === $this->redis->hExists('key''k0'));
        
$this->assertTrue(TRUE === $this->redis->hExists('key''k1'));
        
$this->assertTrue(TRUE === $this->redis->hExists('key''k2'));
        
$this->assertTrue(TRUE === $this->redis->hExists('key''k3'));
        
$this->assertTrue(TRUE === $this->redis->hExists('key''k4'));

        
// hMSet
        
$this->redis->del('key');
        
$this->redis->hMSet('key'$a);
        foreach(
$a as $k => $v) {
            
$this->assertTrue($v === $this->redis->hGet('key'$k));
        }

        
// hMget
        
$hmget $this->redis->hMget('key'array_keys($a));
        foreach(
$hmget as $k => $v) {
            
$this->assertTrue($v === $a[$k]);
        }

        
// getMultiple
        
$this->redis->set('a'NULL);
        
$this->redis->set('b'FALSE);
        
$this->redis->set('c'42);
        
$this->redis->set('d', ['x' => 'y']);

        
$this->assertTrue([NULLFALSE42, ['x' => 'y']] === $this->redis->mGet(['a''b''c''d']));

        
// pipeline
        
if ($this->havePipeline()) {
            
$this->sequence(Redis::PIPELINE);
        }

        
// multi-exec
        
$this->sequence(Redis::MULTI);

        
// keys
        
$this->assertTrue(is_array($this->redis->keys('*')));

        
// issue #62, hgetall
        
$this->redis->del('hash1');
        
$this->redis->hSet('hash1','data''test 1');
        
$this->redis->hSet('hash1','session_id''test 2');

        
$data $this->redis->hGetAll('hash1');
        
$this->assertTrue($data['data'] === 'test 1');
        
$this->assertTrue($data['session_id'] === 'test 2');

        
// issue #145, serializer with objects.
        
$this->redis->set('x', [new stdClass, new stdClass]);
        
$x $this->redis->get('x');
        
$this->assertTrue(is_array($x));
        if (
$mode === Redis::SERIALIZER_JSON) {
            
$this->assertTrue(is_array($x[0]));
            
$this->assertTrue(is_array($x[1]));
        } else {
            
$this->assertTrue(is_object($x[0]) && get_class($x[0]) === 'stdClass');
            
$this->assertTrue(is_object($x[1]) && get_class($x[1]) === 'stdClass');
        }

        
// revert
        
$this->assertTrue($this->redis->setOption(Redis::OPT_SERIALIZERRedis::SERIALIZER_NONE) === TRUE);     // set ok
        
$this->assertTrue($this->redis->getOption(Redis::OPT_SERIALIZER) === Redis::SERIALIZER_NONE);       // get ok
    
}

    public function 
testCompressionLZF()
    {
        if (!
defined('Redis::COMPRESSION_LZF')) {
            
$this->markTestSkipped();
        }

        
/* Don't crash on improperly compressed LZF data */
        
$payload 'not-actually-lzf-compressed';
        
$this->redis->set('badlzf'$payload);
        
$this->redis->setOption(Redis::OPT_COMPRESSIONRedis::COMPRESSION_LZF);
        
$this->assertEquals($payload$this->redis->get('badlzf'));
        
$this->redis->setOption(Redis::OPT_COMPRESSIONRedis::COMPRESSION_NONE);

        
$this->checkCompression(Redis::COMPRESSION_LZF0);
    }

    public function 
testCompressionZSTD()
    {
        if (!
defined('Redis::COMPRESSION_ZSTD')) {
            
$this->markTestSkipped();
        }

        
/* Issue 1936 regression.  Make sure we don't overflow on bad data */
        
$this->redis->del('badzstd');
        
$this->redis->set('badzstd''123');
        
$this->redis->setOption(Redis::OPT_COMPRESSIONRedis::COMPRESSION_ZSTD);
        
$this->assertEquals('123'$this->redis->get('badzstd'));
        
$this->redis->setOption(Redis::OPT_COMPRESSIONRedis::COMPRESSION_NONE);

        
$this->checkCompression(Redis::COMPRESSION_ZSTD0);
        
$this->checkCompression(Redis::COMPRESSION_ZSTD9);
    }


    public function 
testCompressionLZ4()
    {
        if (!
defined('Redis::COMPRESSION_LZ4')) {
            
$this->markTestSkipped();
        }
        
$this->checkCompression(Redis::COMPRESSION_LZ40);
        
$this->checkCompression(Redis::COMPRESSION_LZ49);
    }

    private function 
checkCompression($mode$level)
    {
        
$this->assertTrue($this->redis->setOption(Redis::OPT_COMPRESSION$mode) === TRUE);  // set ok
        
$this->assertTrue($this->redis->getOption(Redis::OPT_COMPRESSION) === $mode);    // get ok

        
$this->assertTrue($this->redis->setOption(Redis::OPT_COMPRESSION_LEVEL$level) === TRUE);
        
$this->assertTrue($this->redis->getOption(Redis::OPT_COMPRESSION_LEVEL) === $level);

        
$val 'xxxxxxxxxx';
        
$this->redis->set('key'$val);
        
$this->assertEquals($val$this->redis->get('key'));

        
/* Empty data */
        
$this->redis->set('key''');
        
$this->assertEquals(''$this->redis->get('key'));

        
/* Iterate through class sizes */
        
for ($i 1$i <= 65536$i *= 2) {
            foreach ([
str_repeat('A'$i), random_bytes($i)] as $val) {
                
$this->redis->set('key'$val);
                
$this->assertEquals($val$this->redis->get('key'));
            }
        }

        
// Issue 1945. Ensure we decompress data with hmget.
        
$this->redis->hset('hkey''data''abc');
        
$this->assertEquals('abc'current($this->redis->hmget('hkey', ['data'])));
    }

    public function 
testDumpRestore() {

        if (
version_compare($this->version"2.5.0") < 0) {
            
$this->markTestSkipped();
        }

        
$this->redis->del('foo');
        
$this->redis->del('bar');

        
$this->redis->set('foo''this-is-foo');
        
$this->redis->set('bar''this-is-bar');

        
$d_foo $this->redis->dump('foo');
        
$d_bar $this->redis->dump('bar');

        
$this->redis->del('foo');
        
$this->redis->del('bar');

        
// Assert returns from restore
        
$this->assertTrue($this->redis->restore('foo'0$d_bar));
        
$this->assertTrue($this->redis->restore('bar'0$d_foo));

        
// Now check that the keys have switched
        
$this->assertTrue($this->redis->get('foo') === 'this-is-bar');
        
$this->assertTrue($this->redis->get('bar') === 'this-is-foo');

        
$this->redis->del('foo');
        
$this->redis->del('bar');
    }

    public function 
testGetLastError() {
        
// We shouldn't have any errors now
        
$this->assertTrue($this->redis->getLastError() === NULL);

        
// test getLastError with a regular command
        
$this->redis->set('x''a');
        
$this->assertFalse($this->redis->incr('x'));
        
$incrError $this->redis->getLastError();
        
$this->assertTrue(strlen($incrError) > 0);

        
// clear error
        
$this->redis->clearLastError();
        
$this->assertTrue($this->redis->getLastError() === NULL);
    }

    
// Helper function to compare nested results -- from the php.net array_diff page, I believe
    
private function array_diff_recursive($aArray1$aArray2) {
        
$aReturn = [];

        foreach (
$aArray1 as $mKey => $mValue) {
            if (
array_key_exists($mKey$aArray2)) {
                if (
is_array($mValue)) {
                    
$aRecursiveDiff $this->array_diff_recursive($mValue$aArray2[$mKey]);
                    if (
count($aRecursiveDiff)) {
                        
$aReturn[$mKey] = $aRecursiveDiff;
                    }
                } else {
                    if (
$mValue != $aArray2[$mKey]) {
                        
$aReturn[$mKey] = $mValue;
                    }
                }
            } else {
                
$aReturn[$mKey] = $mValue;
            }
        }

        return 
$aReturn;
    }

    public function 
testScript() {

        if (
version_compare($this->version"2.5.0") < 0) {
            
$this->markTestSkipped();
        }

        
// Flush any scripts we have
        
$this->assertTrue($this->redis->script('flush'));

        
// Silly scripts to test against
        
$s1_src 'return 1';
        
$s1_sha sha1($s1_src);
        
$s2_src 'return 2';
        
$s2_sha sha1($s2_src);
        
$s3_src 'return 3';
        
$s3_sha sha1($s3_src);

        
// None should exist
        
$result $this->redis->script('exists'$s1_sha$s2_sha$s3_sha);
        
$this->assertTrue(is_array($result) && count($result) == 3);
        
$this->assertTrue(is_array($result) && count(array_filter($result)) == 0);

        
// Load them up
        
$this->assertTrue($this->redis->script('load'$s1_src) == $s1_sha);
        
$this->assertTrue($this->redis->script('load'$s2_src) == $s2_sha);
        
$this->assertTrue($this->redis->script('load'$s3_src) == $s3_sha);

        
// They should all exist
        
$result $this->redis->script('exists'$s1_sha$s2_sha$s3_sha);
        
$this->assertTrue(is_array($result) && count(array_filter($result)) == 3);
    }

    public function 
testEval() {

        if (
version_compare($this->version"2.5.0") < 0) {
            
$this->markTestSkipped();
        }

        
// Basic single line response tests
        
$this->assertTrue(== $this->redis->eval('return 1'));
        
$this->assertTrue(1.55 == $this->redis->eval("return '1.55'"));
        
$this->assertTrue("hello, world" == $this->redis->eval("return 'hello, world'"));

        
/*
         * Keys to be incorporated into lua results
         */
        // Make a list
        
$this->redis->del('{eval-key}-list');
        
$this->redis->rpush('{eval-key}-list''a');
        
$this->redis->rpush('{eval-key}-list''b');
        
$this->redis->rpush('{eval-key}-list''c');

        
// Make a set
        
$this->redis->del('{eval-key}-zset');
        
$this->redis->zadd('{eval-key}-zset'0'd');
        
$this->redis->zadd('{eval-key}-zset'1'e');
        
$this->redis->zadd('{eval-key}-zset'2'f');

        
// Basic keys
        
$this->redis->set('{eval-key}-str1''hello, world');
        
$this->redis->set('{eval-key}-str2''hello again!');

        
// Use a script to return our list, and verify its response
        
$list $this->redis->eval("return redis.call('lrange', KEYS[1], 0, -1)", ['{eval-key}-list'], 1);
        
$this->assertTrue($list === ['a','b','c']);

        
// Use a script to return our zset
        
$zset $this->redis->eval("return redis.call('zrange', KEYS[1], 0, -1)", ['{eval-key}-zset'], 1);
        
$this->assertTrue($zset == ['d','e','f']);

        
// Test an empty MULTI BULK response
        
$this->redis->del('{eval-key}-nolist');
        
$empty_resp $this->redis->eval("return redis.call('lrange', '{eval-key}-nolist', 0, -1)",
            [
'{eval-key}-nolist'], 1);
        
$this->assertTrue(is_array($empty_resp) && empty($empty_resp));

        
// Now test a nested reply
        
$nested_script "
            return {
                1,2,3, {
                    redis.call('get', '{eval-key}-str1'),
                    redis.call('get', '{eval-key}-str2'),
                    redis.call('lrange', 'not-any-kind-of-list', 0, -1),
                    {
                        redis.call('zrange','{eval-key}-zset', 0, -1),
                        redis.call('lrange', '{eval-key}-list', 0, -1)
                    }
                }
            }
        "
;

        
$expected = [
            
123, [
                
'hello, world',
                
'hello again!',
                [],
                [
                    [
'd','e','f'],
                    [
'a','b','c']
                ]
            ]
        ];

        
// Now run our script, and check our values against each other
        
$eval_result $this->redis->eval($nested_script, ['{eval-key}-str1''{eval-key}-str2''{eval-key}-zset''{eval-key}-list'], 4);
        
$this->assertTrue(is_array($eval_result) && count($this->array_diff_recursive($eval_result$expected)) == 0);

        
/*
         * Nested reply wihin a multi/pipeline block
         */

        
$num_scripts 10;

        
$arr_modes = [Redis::MULTI];
        if (
$this->havePipeline()) $arr_modes[] = Redis::PIPELINE;

        foreach(
$arr_modes as $mode) {
            
$this->redis->multi($mode);
            for(
$i=0;$i<$num_scripts;$i++) {
                
$this->redis->eval($nested_script, ['{eval-key}-dummy'], 1);
            }
            
$replies $this->redis->exec();

            foreach(
$replies as $reply) {
                
$this->assertTrue(is_array($reply) && count($this->array_diff_recursive($reply$expected)) == 0);
            }
        }

        
/*
         * KEYS/ARGV
         */

        
$args_script "return {KEYS[1],KEYS[2],KEYS[3],ARGV[1],ARGV[2],ARGV[3]}";
        
$args_args   = ['{k}1','{k}2','{k}3','v1','v2','v3'];
        
$args_result $this->redis->eval($args_script$args_args3);
        
$this->assertTrue($args_result === $args_args);

        
// turn on key prefixing
        
$this->redis->setOption(Redis::OPT_PREFIX'prefix:');
        
$args_result $this->redis->eval($args_script$args_args3);

        
// Make sure our first three are prefixed
        
for($i=0;$i<count($args_result);$i++) {
            if(
$i<3) {
                
// Should be prefixed
                
$this->assertTrue($args_result[$i] == 'prefix:' $args_args[$i]);
            } else {
                
// Should not be prefixed
                
$this->assertTrue($args_result[$i] == $args_args[$i]);
            }
        }
    }

    public function 
testEvalSHA() {
        if (
version_compare($this->version"2.5.0") < 0) {
            
$this->markTestSkipped();
        }

        
// Flush any loaded scripts
        
$this->redis->script('flush');

        
// Non existant script (but proper sha1), and a random (not) sha1 string
        
$this->assertFalse($this->redis->evalsha(sha1(uniqid())));
        
$this->assertFalse($this->redis->evalsha('some-random-data'));

        
// Load a script
        
$cb  uniqid(); // To ensure the script is new
        
$scr "local cb='$cb' return 1";
        
$sha sha1($scr);

        
// Run it when it doesn't exist, run it with eval, and then run it with sha1
        
$this->assertTrue(false === $this->redis->evalsha($scr));
        
$this->assertTrue(=== $this->redis->eval($scr));
        
$this->assertTrue(=== $this->redis->evalsha($sha));
    }

    public function 
testSerialize() {
        
$vals = [11.5'one', ['here','is','an','array']];

        
// Test with no serialization at all
        
$this->assertTrue($this->redis->_serialize('test') === 'test');
        
$this->assertTrue($this->redis->_serialize(1) === '1');
        
$this->assertTrue($this->redis->_serialize([]) === 'Array');
        
$this->assertTrue($this->redis->_serialize(new stdClass) === 'Object');

        
$arr_serializers = [Redis::SERIALIZER_PHP];
        if(
defined('Redis::SERIALIZER_IGBINARY')) {
            
$arr_serializers[] = Redis::SERIALIZER_IGBINARY;
        }

        if(
defined('Redis::SERIALIZER_MSGPACK')) {
            
$arr_serializers[] = Redis::SERIALIZER_MSGPACK;
        }

        foreach(
$arr_serializers as $mode) {
            
$arr_enc = [];
            
$arr_dec = [];

            foreach(
$vals as $k => $v) {
                
$enc $this->redis->_serialize($v);
                
$dec $this->redis->_unserialize($enc);

                
// They should be the same
                
$this->assertTrue($enc == $dec);
            }
        }
    }

    public function 
testUnserialize() {
        
$vals = [
            
1,1.5,'one',['this','is','an','array']
        ];

        
$serializers = Array(Redis::SERIALIZER_PHP);

        if(
defined('Redis::SERIALIZER_IGBINARY')) {
            
$serializers[] = Redis::SERIALIZER_IGBINARY;
        }

        if(
defined('Redis::SERIALIZER_MSGPACK')) {
            
$serializers[] = Redis::SERIALIZER_MSGPACK;
        }

        foreach(
$serializers as $mode) {
            
$vals_enc = [];

            
// Pass them through redis so they're serialized
            
foreach($vals as $key => $val) {
                
$this->redis->setOption(Redis::OPT_SERIALIZER$mode);

                
$key "key" . ++$key;
                
$this->redis->del($key);
                
$this->redis->set($key$val);

                
// Clear serializer, get serialized value
                
$this->redis->setOption(Redis::OPT_SERIALIZERRedis::SERIALIZER_NONE);
                
$vals_enc[] = $this->redis->get($key);
            }

            
// Run through our array comparing values
            
for($i=0;$i<count($vals);$i++) {
                
// reset serializer
                
$this->redis->setOption(Redis::OPT_SERIALIZER$mode);
                
$this->assertTrue($vals[$i] == $this->redis->_unserialize($vals_enc[$i]));
                
$this->redis->setOption(Redis::OPT_SERIALIZERRedis::SERIALIZER_NONE);
            }
        }
    }

    public function 
testCompressHelpers() {
        
$compressors self::getAvailableCompression();

        
$vals = ['foo'12345random_bytes(128), ''];

        
$oldcmp $this->redis->getOption(Redis::OPT_COMPRESSION);

        foreach (
$compressors as $cmp) {
            foreach (
$vals as $val) {
                
$this->redis->setOption(Redis::OPT_COMPRESSION$cmp);
                
$this->redis->set('cmpkey'$val);

                
/* Get the value raw */
                
$this->redis->setOption(Redis::OPT_COMPRESSIONRedis::COMPRESSION_NONE);
                
$raw $this->redis->get('cmpkey');
                
$this->redis->setOption(Redis::OPT_COMPRESSION$cmp);

                
$this->assertEquals($raw$this->redis->_compress($val));

                
$uncompressed $this->redis->get('cmpkey');
                
$this->assertEquals($uncompressed$this->redis->_uncompress($raw));
            }
        }

        
$this->redis->setOption(Redis::OPT_COMPRESSION$oldcmp);
    }

    public function 
testPackHelpers() {
        list (
$oldser$oldcmp) = [
            
$this->redis->getOption(Redis::OPT_SERIALIZER),
            
$this->redis->getOption(Redis::OPT_COMPRESSION)
        ];

        foreach (
$this->serializers as $ser) {
            
$compressors self::getAvailableCompression();
            foreach (
$compressors as $cmp) {
                
$this->redis->setOption(Redis::OPT_SERIALIZER$ser);
                
$this->redis->setOption(Redis::OPT_COMPRESSION$cmp);

        foreach ([
'foo'12345random_bytes(128), '', ['an''array']] as $v) {
                    
/* Can only attempt the array if we're serializing */
                    
if (is_array($v) && $ser == Redis::SERIALIZER_NONE)
                        continue;

                    
$this->redis->set('packkey'$v);

                    
/* Get the value raw */
                    
$this->redis->setOption(Redis::OPT_SERIALIZERRedis::SERIALIZER_NONE);
                    
$this->redis->setOption(Redis::OPT_COMPRESSIONRedis::COMPRESSION_NONE);
                    
$raw $this->redis->get('packkey');
                    
$this->redis->setOption(Redis::OPT_SERIALIZER$ser);
                    
$this->redis->setOption(Redis::OPT_COMPRESSION$cmp);

                    
$this->assertEquals($raw$this->redis->_pack($v));

                    
$unpacked $this->redis->get('packkey');
            
$this->assertEquals($unpacked$this->redis->_unpack($raw));
        }
        }
        }

        
$this->redis->setOption(Redis::OPT_SERIALIZER$oldser);
        
$this->redis->setOption(Redis::OPT_COMPRESSION$oldcmp);
    }

    public function 
testPrefix() {
        
// no prefix
        
$this->redis->setOption(Redis::OPT_PREFIX'');
        
$this->assertTrue('key' == $this->redis->_prefix('key'));

        
// with a prefix
        
$this->redis->setOption(Redis::OPT_PREFIX'some-prefix:');
        
$this->assertTrue('some-prefix:key' == $this->redis->_prefix('key'));

        
// Clear prefix
        
$this->redis->setOption(Redis::OPT_PREFIX'');

    }

    public function 
testReplyLiteral() {
        
$this->redis->setOption(Redis::OPT_REPLY_LITERALfalse);
        
$this->assertTrue($this->redis->rawCommand('set''foo''bar'));
        
$this->assertTrue($this->redis->eval("return redis.call('set', 'foo', 'bar')", [], 0));

        
$rv $this->redis->eval("return {redis.call('set', KEYS[1], 'bar'), redis.call('ping')}", ['foo'], 1);
        
$this->assertEquals([truetrue], $rv);

        
$this->redis->setOption(Redis::OPT_REPLY_LITERALtrue);
        
$this->assertEquals('OK'$this->redis->rawCommand('set''foo''bar'));
        
$this->assertEquals('OK'$this->redis->eval("return redis.call('set', 'foo', 'bar')", [], 0));

        
// Nested
        
$rv $this->redis->eval("return {redis.call('set', KEYS[1], 'bar'), redis.call('ping')}", ['foo'], 1);
        
$this->assertEquals(['OK''PONG'], $rv);

        
// Reset
        
$this->redis->setOption(Redis::OPT_REPLY_LITERALfalse);
    }

    
/* Test that we can configure PhpRedis to return NULL for *-1 even nestedwithin replies */
    
public function testNestedNullArray() {
        
$this->redis->del('{notaset}');

        foreach ([
false => [], true => NULL] as $opt => $test) {
            
$this->redis->setOption(Redis::OPT_NULL_MULTIBULK_AS_NULL$opt);
            
$this->assertEquals([$test$test], $this->redis->geoPos('{notaset}''m1''m2'));

            
$this->redis->multi();
            
$this->redis->geoPos('{notaset}''m1''m2');
            
$this->assertEquals([[$test$test]], $this->redis->exec());
        }

        
$this->redis->setOption(Redis::OPT_NULL_MULTIBULK_AS_NULLfalse);
    }

    public function 
testReconnectSelect() {
        
$key 'reconnect-select';
        
$value 'Has been set!';

        
$original_cfg $this->redis->config('GET''timeout');

        
// Make sure the default DB doesn't have the key.
        
$this->redis->select(0);
        
$this->redis->del($key);

        
// Set the key on a different DB.
        
$this->redis->select(5);
        
$this->redis->set($key$value);

        
// Time out after 1 second.
        
$this->redis->config('SET''timeout''1');

        
// Wait for the timeout. With Redis 2.4, we have to wait up to 10 s
        // for the server to close the connection, regardless of the timeout
        // setting.
        
sleep(11);

        
// Make sure we're still using the same DB.
        
$this->assertEquals($value$this->redis->get($key));

        
// Revert the setting.
        
$this->redis->config('SET''timeout'$original_cfg['timeout']);
    }

    public function 
testTime() {

        if (
version_compare($this->version"2.5.0") < 0) {
            
$this->markTestSkipped();
        }

        
$time_arr $this->redis->time();
        
$this->assertTrue(is_array($time_arr) && count($time_arr) == &&
                          
strval(intval($time_arr[0])) === strval($time_arr[0]) &&
                          
strval(intval($time_arr[1])) === strval($time_arr[1]));
    }

    public function 
testReadTimeoutOption() {

        
$this->assertTrue(defined('Redis::OPT_READ_TIMEOUT'));

        
$this->redis->setOption(Redis::OPT_READ_TIMEOUT"12.3");
        
$this->assertEquals(12.3$this->redis->getOption(Redis::OPT_READ_TIMEOUT));
    }

    public function 
testIntrospection() {
        
// Simple introspection tests
        
$this->assertTrue($this->redis->getHost() === $this->getHost());
        
$this->assertTrue($this->redis->getPort() === $this->getPort());
        
$this->assertTrue($this->redis->getAuth() === $this->getAuth());
    }

    
/**
     * Scan and variants
     */

    
protected function get_keyspace_count($str_db) {
        
$arr_info $this->redis->info();
        if (isset(
$arr_info[$str_db])) {
            
$arr_info $arr_info[$str_db];
            
$arr_info explode(','$arr_info);
            
$arr_info explode('='$arr_info[0]);
            return 
$arr_info[1];
        } else {
            return 
0;
        }
    }

    public function 
testScan() {
        if(
version_compare($this->version"2.8.0") < 0) {
            
$this->markTestSkipped();
            return;
        }

        
// Key count
        
$i_key_count $this->get_keyspace_count('db0');

        
// Have scan retry
        
$this->redis->setOption(Redis::OPT_SCANRedis::SCAN_RETRY);

        
// Scan them all
        
$it NULL;
        while(
$arr_keys $this->redis->scan($it)) {
            
$i_key_count -= count($arr_keys);
        }
        
// Should have iterated all keys
        
$this->assertEquals(0$i_key_count);

        
// Unique keys, for pattern matching
        
$str_uniq uniqid() . '-' uniqid();
        for(
$i=0;$i<10;$i++) {
            
$this->redis->set($str_uniq "::$i""bar::$i");
        }

        
// Scan just these keys using a pattern match
        
$it NULL;
        while(
$arr_keys $this->redis->scan($it"*$str_uniq*")) {
            
$i -= count($arr_keys);
        }
        
$this->assertEquals(0$i);

        
// SCAN with type is scheduled for release in Redis 6.
        
if (version_compare($this->version"6.0.0") >= 0) {
            
// Use a unique ID so we can find our type keys
            
$id uniqid();

            
// Create some simple keys and lists
            
for ($i 0$i 3$i++) {
                
$str_simple "simple:{$id}:$i";
                
$str_list "list:{$id}:$i";

                
$this->redis->set($str_simple$i);
                
$this->redis->del($str_list);
                
$this->redis->rpush($str_list, ['foo']);

                
$arr_keys["STRING"][] = $str_simple;
                
$arr_keys["LIST"][] = $str_list;
            }

            
// Make sure we can scan for specific types
            
foreach ($arr_keys as $str_type => $arr_vals) {
                foreach ([
NULL10] as $i_count) {
                    
$arr_resp = [];

                    
$it NULL;
                    while (
$arr_scan $this->redis->scan($it"*$id*"$i_count$str_type)) {
                        
$arr_resp array_merge($arr_resp$arr_scan);
                    }

                    
sort($arr_vals); sort($arr_resp);
                    
$this->assertEquals($arr_vals$arr_resp);
                }
            }
        }
    }

    public function 
testScanPrefix() {
        
$keyid uniqid();

        
/* Set some keys with different prefixes */
        
$arr_prefixes = ['prefix-a:''prefix-b:'];
        foreach (
$arr_prefixes as $str_prefix) {
            
$this->redis->setOption(Redis::OPT_PREFIX$str_prefix);
            
$this->redis->set("$keyid""LOLWUT");
            
$arr_all_keys["${str_prefix}${keyid}"] = true;
        }

        
$this->redis->setOption(Redis::OPT_SCANRedis::SCAN_RETRY);
        
$this->redis->setOption(Redis::OPT_SCANRedis::SCAN_PREFIX);

        foreach (
$arr_prefixes as $str_prefix) {
            
$this->redis->setOption(Redis::OPT_PREFIX$str_prefix);
            
$it NULL;
            
$arr_keys $this->redis->scan($it"*$keyid*");
            
$this->assertEquals($arr_keys, ["${str_prefix}${keyid}"]);
        }

        
/* Unset the prefix option */
        
$this->redis->setOption(Redis::OPT_SCANRedis::SCAN_NOPREFIX);

        
$it NULL;
        while (
$arr_keys $this->redis->scan($it"*$keyid*")) {
            foreach (
$arr_keys as $str_key) {
                unset(
$arr_all_keys[$str_key]);
            }
        }

        
/* Should have touched every key */
        
$this->assertTrue(count($arr_all_keys) == 0);
    }

    public function 
testMaxRetriesOption() {
        
$maxRetriesExpected 5;
        
$this->redis->setOption(Redis::OPT_MAX_RETRIES$maxRetriesExpected);
        
$maxRetriesActual=$this->redis->getOption(Redis::OPT_MAX_RETRIES);
        
$this->assertEquals($maxRetriesActual$maxRetriesExpected);
    }

    public function 
testBackoffOptions() {
        
$this->redis->setOption(Redis::OPT_BACKOFF_ALGORITHMRedis::BACKOFF_ALGORITHM_DEFAULT);
        
$this->assertEquals($this->redis->getOption(Redis::OPT_BACKOFF_ALGORITHM), Redis::BACKOFF_ALGORITHM_DEFAULT);

        
$this->redis->setOption(Redis::OPT_BACKOFF_ALGORITHMRedis::BACKOFF_ALGORITHM_CONSTANT);
        
$this->assertEquals($this->redis->getOption(Redis::OPT_BACKOFF_ALGORITHM), Redis::BACKOFF_ALGORITHM_CONSTANT);

        
$this->redis->setOption(Redis::OPT_BACKOFF_ALGORITHMRedis::BACKOFF_ALGORITHM_UNIFORM);
        
$this->assertEquals($this->redis->getOption(Redis::OPT_BACKOFF_ALGORITHM), Redis::BACKOFF_ALGORITHM_UNIFORM);

        
$this->redis -> setOption(Redis::OPT_BACKOFF_ALGORITHMRedis::BACKOFF_ALGORITHM_EXPONENTIAL);
        
$this->assertEquals($this->redis->getOption(Redis::OPT_BACKOFF_ALGORITHM), Redis::BACKOFF_ALGORITHM_EXPONENTIAL);

        
$this->redis->setOption(Redis::OPT_BACKOFF_ALGORITHMRedis::BACKOFF_ALGORITHM_EQUAL_JITTER);
        
$this->assertEquals($this->redis->getOption(Redis::OPT_BACKOFF_ALGORITHM), Redis::BACKOFF_ALGORITHM_EQUAL_JITTER);

        
$this->redis->setOption(Redis::OPT_BACKOFF_ALGORITHMRedis::BACKOFF_ALGORITHM_FULL_JITTER);
        
$this->assertEquals($this->redis->getOption(Redis::OPT_BACKOFF_ALGORITHM), Redis::BACKOFF_ALGORITHM_FULL_JITTER);

        
$this->redis->setOption(Redis::OPT_BACKOFF_ALGORITHMRedis::BACKOFF_ALGORITHM_DECORRELATED_JITTER);
        
$this->assertEquals($this->redis->getOption(Redis::OPT_BACKOFF_ALGORITHM), Redis::BACKOFF_ALGORITHM_DECORRELATED_JITTER);

        
$this->assertFalse($this->redis->setOption(Redis::OPT_BACKOFF_ALGORITHM55555));

        
$this->redis->setOption(Redis::OPT_BACKOFF_BASE500);
        
$this->assertEquals($this->redis->getOption(Redis::OPT_BACKOFF_BASE), 500);

        
$this->redis->setOption(Redis::OPT_BACKOFF_BASE750);
        
$this->assertEquals($this->redis->getOption(Redis::OPT_BACKOFF_BASE), 750);

        
$this->redis->setOption(Redis::OPT_BACKOFF_CAP500);
        
$this->assertEquals($this->redis->getOption(Redis::OPT_BACKOFF_CAP), 500);

        
$this->redis->setOption(Redis::OPT_BACKOFF_CAP750);
        
$this->assertEquals($this->redis->getOption(Redis::OPT_BACKOFF_CAP), 750);
    }

    public function 
testHScan() {
        if (
version_compare($this->version"2.8.0") < 0) {
            
$this->markTestSkipped();
            return;
        }

        
// Never get empty sets
        
$this->redis->setOption(Redis::OPT_SCANRedis::SCAN_RETRY);

        
$this->redis->del('hash');
        
$i_foo_mems 0;

        for(
$i=0;$i<100;$i++) {
            if(
$i>3) {
                
$this->redis->hset('hash'"member:$i""value:$i");
            } else {
                
$this->redis->hset('hash'"foomember:$i""value:$i");
                
$i_foo_mems++;
            }
        }

        
// Scan all of them
        
$it NULL;
        while(
$arr_keys $this->redis->hscan('hash'$it)) {
            
$i -= count($arr_keys);
        }
        
$this->assertEquals(0$i);

        
// Scan just *foomem* (should be 4)
        
$it NULL;
        while(
$arr_keys $this->redis->hscan('hash'$it'*foomember*')) {
            
$i_foo_mems -= count($arr_keys);
            foreach(
$arr_keys as $str_mem => $str_val) {
                
$this->assertTrue(strpos($str_mem'member')!==FALSE);
                
$this->assertTrue(strpos($str_val'value')!==FALSE);
            }
        }
        
$this->assertEquals(0$i_foo_mems);
    }

    public function 
testSScan() {
        if (
version_compare($this->version"2.8.0") < 0) {
            
$this->markTestSkipped();
            return;
        }

        
$this->redis->setOption(Redis::OPT_SCANRedis::SCAN_RETRY);

        
$this->redis->del('set');
        for(
$i=0;$i<100;$i++) {
            
$this->redis->sadd('set'"member:$i");
        }

        
// Scan all of them
        
$it NULL;
        while(
$arr_keys $this->redis->sscan('set'$it)) {
            
$i -= count($arr_keys);
            foreach(
$arr_keys as $str_mem) {
                
$this->assertTrue(strpos($str_mem,'member')!==FALSE);
            }
        }
        
$this->assertEquals(0$i);

        
// Scan just ones with zero in them (0, 10, 20, 30, 40, 50, 60, 70, 80, 90)
        
$it NULL;
        
$i_w_zero 0;
        while(
$arr_keys $this->redis->sscan('set'$it'*0*')) {
            
$i_w_zero += count($arr_keys);
        }
        
$this->assertEquals(10$i_w_zero);
    }

    public function 
testZScan() {
        if (
version_compare($this->version"2.8.0") < 0) {
            
$this->markTestSkipped();
            return;
        }

        
$this->redis->setOption(Redis::OPT_SCANRedis::SCAN_RETRY);

        
$this->redis->del('zset');
        
$i_tot_score 0;
        
$i_p_score 0;
        
$i_p_count 0;
        for(
$i=0;$i<2000;$i++) {
            if(
$i<10) {
                
$this->redis->zadd('zset'$i"pmem:$i");
                
$i_p_score += $i;
                
$i_p_count += 1;
            } else {
                
$this->redis->zadd('zset'$i"mem:$i");
            }

            
$i_tot_score += $i;
        }

        
// Scan them all
        
$it NULL;
        while(
$arr_keys $this->redis->zscan('zset'$it)) {
            foreach(
$arr_keys as $str_mem => $f_score) {
                
$i_tot_score -= $f_score;
                
$i--;
            }
        }

        
$this->assertEquals(0$i);
        
$this->assertEquals((float)0$i_tot_score);

        
// Just scan "pmem" members
        
$it NULL;
        
$i_p_score_old $i_p_score;
        
$i_p_count_old $i_p_count;
        while(
$arr_keys $this->redis->zscan('zset'$it"*pmem*")) {
            foreach(
$arr_keys as $str_mem => $f_score) {
                
$i_p_score -= $f_score;
                
$i_p_count -= 1;
            }
        }
        
$this->assertEquals((float)0$i_p_score);
        
$this->assertEquals(0$i_p_count);

        
// Turn off retrying and we should get some empty results
        
$this->redis->setOption(Redis::OPT_SCANRedis::SCAN_NORETRY);
        
$i_skips 0;
        
$i_p_score $i_p_score_old;
        
$i_p_count $i_p_count_old;
        
$it NULL;
        while((
$arr_keys $this->redis->zscan('zset'$it"*pmem*")) !== FALSE) {
            if(
count($arr_keys) == 0$i_skips++;
            foreach(
$arr_keys as $str_mem => $f_score) {
                
$i_p_score -= $f_score;
                
$i_p_count -= 1;
            }
        }
        
// We should still get all the keys, just with several empty results
        
$this->assertTrue($i_skips 0);
        
$this->assertEquals((float)0$i_p_score);
        
$this->assertEquals(0$i_p_count);
    }

    
//
    // HyperLogLog (PF) commands
    //

    
protected function createPFKey($str_key$i_count) {
        
$arr_mems = [];
        for(
$i=0;$i<$i_count;$i++) {
            
$arr_mems[] = uniqid() . '-' $i;
        }

        
// Estimation by Redis
        
$this->redis->pfadd($str_key$i_count);
    }

    public function 
testPFCommands() {
        
// Isn't available until 2.8.9
        
if (version_compare($this->version"2.8.9") < 0) {
            
$this->markTestSkipped();
            return;
        }

        
$str_uniq uniqid();
        
$arr_mems = [];

        for(
$i=0;$i<1000;$i++) {
            if(
$i%== 0) {
                
$arr_mems[] = $str_uniq '-' $i;
            } else {
                
$arr_mems[] = $i;
            }
        }

        
// How many keys to create
        
$i_keys 10;

        
// Iterate prefixing/serialization options
        
foreach([Redis::SERIALIZER_NONERedis::SERIALIZER_PHP] as $str_ser) {
            foreach([
'''hl-key-prefix:'] as $str_prefix) {
                
$arr_keys = [];

                
// Now add for each key
                
for($i=0;$i<$i_keys;$i++) {
                    
$str_key    "{key}:$i";
                    
$arr_keys[] = $str_key;

                    
// Clean up this key
                    
$this->redis->del($str_key);

                    
// Add to our cardinality set, and confirm we got a valid response
                    
$this->assertTrue($this->redis->pfadd($str_key$arr_mems));

                    
// Grab estimated cardinality
                    
$i_card $this->redis->pfcount($str_key);
                    
$this->assertTrue(is_int($i_card));

                    
// Count should be close
                    
$this->assertLess(abs($i_card-count($arr_mems)), count($arr_mems) * .1);

                    
// The PFCOUNT on this key should be the same as the above returned response
                    
$this->assertEquals($this->redis->pfcount($str_key), $i_card);
                }

                
// Clean up merge key
                
$this->redis->del('pf-merge-{key}');

                
// Merge the counters
                
$this->assertTrue($this->redis->pfmerge('pf-merge-{key}'$arr_keys));

                
// Validate our merged count
                
$i_redis_card $this->redis->pfcount('pf-merge-{key}');

                
// Merged cardinality should still be roughly 1000
                
$this->assertLess(abs($i_redis_card-count($arr_mems)), count($arr_mems) * .1);

                
// Clean up merge key
                
$this->redis->del('pf-merge-{key}');
            }
        }
    }

    
//
    // GEO* command tests
    //

    
protected function rawCommandArray($key$args) {
        return 
call_user_func_array([$this->redis'rawCommand'], $args);
    }

    protected function 
addCities($key) {
        
$this->redis->del($key);
        foreach (
$this->cities as $city => $longlat) {
            
$this->redis->geoadd($key$longlat[0], $longlat[1], $city);
        }
    }

    
/* GEOADD */
    
public function testGeoAdd() {
        if (!
$this->minVersionCheck("3.2")) {
            return 
$this->markTestSkipped();
        }

        
$this->redis->del('geokey');

        
/* Add them one at a time */
        
foreach ($this->cities as $city => $longlat) {
            
$this->assertEquals($this->redis->geoadd('geokey'$longlat[0], $longlat[1], $city), 1);
        }

        
/* Add them again, all at once */
        
$args = ['geokey'];
        foreach (
$this->cities as $city => $longlat) {
            
$args array_merge($args, [$longlat[0], $longlat[1], $city]);
        }

        
/* They all exist, should be nothing added */
        
$this->assertEquals(call_user_func_array([$this->redis'geoadd'], $args), 0);
    }

    
/* GEORADIUS */
    
public function genericGeoRadiusTest($cmd) {
        if (!
$this->minVersionCheck("3.2.0")) {
            return 
$this->markTestSkipped();
        }

        
/* Chico */
        
$city 'Chico';
        
$lng = -121.837478;
        
$lat 39.728494;

        
$this->addCities('{gk}');

        
/* Pre tested with redis-cli.  We're just verifying proper delivery of distance and unit */
        
if ($cmd == 'georadius' || $cmd == 'georadius_ro') {
            
$this->assertEquals($this->redis->$cmd('{gk}'$lng$lat10'mi'), Array('Chico'));
            
$this->assertEquals($this->redis->$cmd('{gk}'$lng$lat30'mi'), Array('Gridley','Chico'));
            
$this->assertEquals($this->redis->$cmd('{gk}'$lng$lat50'km'), Array('Gridley','Chico'));
            
$this->assertEquals($this->redis->$cmd('{gk}'$lng$lat50000'm'), Array('Gridley','Chico'));
            
$this->assertEquals($this->redis->$cmd('{gk}'$lng$lat150000'ft'), Array('Gridley''Chico'));
            
$args = Array($cmd'{gk}'$lng$lat500'mi');

            
/* Test a bad COUNT argument */
            
foreach (Array(-10'notanumber') as $count) {
                
$this->assertFalse(@$this->redis->$cmd('{gk}'$lng$lat10'mi', Array('count' => $count)));
            }
        } else {
            
$this->assertEquals($this->redis->$cmd('{gk}'$city10'mi'), Array('Chico'));
            
$this->assertEquals($this->redis->$cmd('{gk}'$city30'mi'), Array('Gridley','Chico'));
            
$this->assertEquals($this->redis->$cmd('{gk}'$city50'km'), Array('Gridley','Chico'));
            
$this->assertEquals($this->redis->$cmd('{gk}'$city50000'm'), Array('Gridley','Chico'));
            
$this->assertEquals($this->redis->$cmd('{gk}'$city150000'ft'), Array('Gridley''Chico'));
            
$args = Array($cmd'{gk}'$city500'mi');

            
/* Test a bad COUNT argument */
            
foreach (Array(-10'notanumber') as $count) {
                
$this->assertFalse(@$this->redis->$cmd('{gk}'$city10'mi', Array('count' => $count)));
            }
        }

        
/* Options */
        
$opts = ['WITHCOORD''WITHDIST''WITHHASH'];
        
$sortopts = ['''ASC''DESC'];
        
$storeopts = ['''STORE''STOREDIST'];

        for (
$i 0$i count($opts); $i++) {
            
$subopts array_slice($opts0$i);
            
shuffle($subopts);

            
$subargs $args;
            foreach (
$subopts as $opt) {
                
$subargs[] = $opt;
            }

            
/* Cannot mix STORE[DIST] with the WITH* arguments */
            
$realstoreopts count($subopts) == $storeopts : [];

            
$base_subargs $subargs;
            
$base_subopts $subopts;

            foreach (
$realstoreopts as $store_type) {

                for (
$c 0$c 3$c++) {
                    
$subargs $base_subargs;
                    
$subopts $base_subopts;

                    
/* Add a count if we're past first iteration */
                    
if ($c 0) {
                        
$subopts['count'] = $c;
                        
$subargs[] = 'count';
                        
$subargs[] = $c;
                    }

                    
/* Adding optional sort */
                    
foreach ($sortopts as $sortopt) {
                        
$realargs $subargs;
                        
$realopts $subopts;

                        if (
$sortopt) {
                            
$realargs[] = $sortopt;
                            
$realopts[] = $sortopt;
                        }

                        if (
$store_type) {
                            
$realopts[$store_type] = "{gk}-$store_type";
                            
$realargs[] = $store_type;
                            
$realargs[] = "{gk}-$store_type";
                        }

                        
$ret1 $this->rawCommandArray('{gk}'$realargs);
                        if (
$cmd == 'georadius' || $cmd == 'georadius_ro') {
                            
$ret2 $this->redis->$cmd('{gk}'$lng$lat500'mi'$realopts);
                        } else {
                            
$ret2 $this->redis->$cmd('{gk}'$city500'mi'$realopts);
                        }

                        
$this->assertEquals($ret1$ret2);
                    }
                }
            }
        }
    }

    public function 
testGeoRadius() {
        if (!
$this->minVersionCheck("3.2.0")) {
            return 
$this->markTestSkipped();
        }

        
$this->genericGeoRadiusTest('georadius');
        
$this->genericGeoRadiusTest('georadius_ro');
    }

    public function 
testGeoRadiusByMember() {
        if (!
$this->minVersionCheck("3.2.0")) {
            return 
$this->markTestSkipped();
        }

        
$this->genericGeoRadiusTest('georadiusbymember');
        
$this->genericGeoRadiusTest('georadiusbymember_ro');
    }

    public function 
testGeoPos() {
        if (!
$this->minVersionCheck("3.2.0")) {
            return 
$this->markTestSkipped();
        }

        
$this->addCities('gk');
        
$this->assertEquals($this->redis->geopos('gk''Chico''Sacramento'), $this->rawCommandArray('gk', ['geopos''gk''Chico''Sacramento']));
        
$this->assertEquals($this->redis->geopos('gk''Cupertino'), $this->rawCommandArray('gk', ['geopos''gk''Cupertino']));
    }

    public function 
testGeoHash() {
        if (!
$this->minVersionCheck("3.2.0")) {
            return 
$this->markTestSkipped();
        }

        
$this->addCities('gk');
        
$this->assertEquals($this->redis->geohash('gk''Chico''Sacramento'), $this->rawCommandArray('gk', ['geohash''gk''Chico''Sacramento']));
        
$this->assertEquals($this->redis->geohash('gk''Chico'), $this->rawCommandArray('gk', ['geohash''gk''Chico']));
    }

    public function 
testGeoDist() {
        if (!
$this->minVersionCheck("3.2.0")) {
            return 
$this->markTestSkipped();
        }

        
$this->addCities('gk');

        
$r1 $this->redis->geodist('gk''Chico''Cupertino');
        
$r2 $this->rawCommandArray('gk', ['geodist''gk''Chico''Cupertino']);
        
$this->assertEquals(round($r18), round($r28));

        
$r1 $this->redis->geodist('gk''Chico''Cupertino''km');
        
$r2 $this->rawCommandArray('gk', ['geodist''gk''Chico''Cupertino''km']);
        
$this->assertEquals(round($r18), round($r28));
    }

    
/* Test a 'raw' command */
    
public function testRawCommand() {
        
$this->redis->set('mykey','some-value');
        
$str_result $this->redis->rawCommand('get''mykey');
        
$this->assertEquals($str_result'some-value');

        
$this->redis->del('mylist');
        
$this->redis->rpush('mylist''A''B''C''D');
        
$this->assertEquals($this->redis->lrange('mylist'0, -1), ['A','B','C','D']);
    }

    
/* STREAMS */

    
protected function addStreamEntries($key$count) {
        
$ids = [];

        
$this->redis->del($key);

        for (
$i 0$i $count$i++) {
            
$ids[] = $this->redis->xAdd($key'*', ['field' => "value:$i"]);
        }

        return 
$ids;
    }

    protected function 
addStreamsAndGroups($arr_streams$count$arr_groups) {
        
$ids = [];

        foreach (
$arr_streams as $str_stream) {
            
$ids[$str_stream] = $this->addStreamEntries($str_stream$count);
            foreach (
$arr_groups as $str_group => $str_id) {
                
$this->redis->xGroup('CREATE'$str_stream$str_group$str_id);
            }
        }

        return 
$ids;
    }

    public function 
testXAdd() {
        if (!
$this->minVersionCheck("5.0"))
            return 
$this->markTestSkipped();

        
$this->redis->del('stream');
        for (
$i 0$i 5$i++) {
            
$id $this->redis->xAdd("stream"'*', ['k1' => 'v1''k2' => 'v2']);
            
$this->assertEquals($this->redis->xLen('stream'), $i+1);

            
/* Redis should return <timestamp>-<sequence> */
            
$bits explode('-'$id);
            
$this->assertEquals(count($bits), 2);
            
$this->assertTrue(is_numeric($bits[0]));
            
$this->assertTrue(is_numeric($bits[1]));
        }

        
/* Test an absolute maximum length */
        
for ($i 0$i 100$i++) {
            
$this->redis->xAdd('stream''*', ['k' => 'v'], 10);
        }
        
$this->assertEquals($this->redis->xLen('stream'), 10);

        
/* Not the greatest test but I'm unsure if approximate trimming is
         * totally deterministic, so just make sure we are able to add with
         * an approximate maxlen argument structure */
        
$id $this->redis->xAdd('stream''*', ['k' => 'v'], 10true);
        
$this->assertEquals(count(explode('-'$id)), 2);

        
/* Empty message should fail */
        
$this->redis->xAdd('stream''*', []);
    }

    protected function 
doXRangeTest($reverse) {
        
$key '{stream}';

        if (
$reverse) {
            list(
$cmd,$a1,$a2) = ['xRevRange''+'0];
        } else {
            list(
$cmd,$a1,$a2) = ['xRange'0'+'];
        }

        
$this->redis->del($key);
        for (
$i 0$i 3$i++) {
            
$msg = ['field' => "value:$i"];
            
$id $this->redis->xAdd($key'*'$msg);
            
$rows[$id] = $msg;
        }

        
$messages $this->redis->$cmd($key$a1$a2);
        
$this->assertEquals(count($messages), 3);

        
$i $reverse 0;
        foreach (
$messages as $seq => $v) {
            
$this->assertEquals(count(explode('-'$seq)), 2);
            
$this->assertEquals($v, ['field' => "value:$i"]);
            
$i += $reverse ? -1;
        }

        
/* Test COUNT option */
        
for ($count 1$count <= 3$count++) {
            
$messages $this->redis->$cmd($key$a1$a2$count);
            
$this->assertEquals(count($messages), $count);
        }
    }

    public function 
testXRange() {
        if (!
$this->minVersionCheck("5.0"))
            return 
$this->markTestSkipped();

        foreach ([
falsetrue] as $reverse) {
            foreach (
$this->serializers as $serializer) {
                foreach ([
NULL'prefix:'] as $prefix) {
                    
$this->redis->setOption(Redis::OPT_PREFIX$prefix);
                    
$this->redis->setOption(Redis::OPT_SERIALIZER$serializer);
                    
$this->doXRangeTest($reverse);
                }
            }
        }
    }

    protected function 
testXLen() {
        if (!
$this->minVersionCheck("5.0"))
            
$this->markTestSkipped();

        
$this->redis->del('{stream}');
        for (
$i 0$i 5$i++) {
            
$this->redis->xadd('{stream}''*', ['foo' => 'bar']);
            
$this->assertEquals($this->redis->xLen('{stream}'), $i+1);
        }
    }

    public function 
testXGroup() {
        if (!
$this->minVersionCheck("5.0"))
            return 
$this->markTestSkipped();

        
/* CREATE MKSTREAM */
        
$str_key 's:' uniqid();
        
$this->assertFalse($this->redis->xGroup('CREATE'$str_key'g0'0));
        
$this->assertTrue($this->redis->xGroup('CREATE'$str_key'g1'0true));

        
/* XGROUP DESTROY */
        
$this->assertEquals($this->redis->xGroup('DESTROY'$str_key'g1'), 1);

        
/* Populate some entries in stream 's' */
        
$this->addStreamEntries('s'2);

        
/* CREATE */
        
$this->assertTrue($this->redis->xGroup('CREATE''s''mygroup''$'));
        
$this->assertFalse($this->redis->xGroup('CREATE''s''mygroup''BAD_ID'));

        
/* BUSYGROUP */
        
$this->redis->xGroup('CREATE''s''mygroup''$');
        
$this->assertTrue(strpos($this->redis->getLastError(), 'BUSYGROUP') === 0);

        
/* SETID */
        
$this->assertTrue($this->redis->xGroup('SETID''s''mygroup''$'));
        
$this->assertFalse($this->redis->xGroup('SETID''s''mygroup''BAD_ID'));

        
$this->assertEquals($this->redis->xGroup('DELCONSUMER''s''mygroup''myconsumer'),0);
    }

    public function 
testXAck() {
        if (!
$this->minVersionCheck("5.0"))
            return 
$this->markTestSkipped();

        for (
$n 1$n <= 3$n++) {
            
$this->addStreamsAndGroups(['{s}'], 3, ['g1' => 0]);
            
$msg $this->redis->xReadGroup('g1''c1', ['{s}' => '>']);

            
/* Extract IDs */
            
$smsg array_shift($msg);
            
$ids array_keys($smsg);

            
/* Now ACK $n messages */
            
$ids array_slice($ids0$n);
            
$this->assertEquals($this->redis->xAck('{s}''g1'$ids), $n);
        }

        
/* Verify sending no IDs is a failure */
        
$this->assertFalse($this->redis->xAck('{s}''g1', []));
    }

    protected function 
doXReadTest() {
        if (!
$this->minVersionCheck("5.0"))
            return 
$this->markTestSkipped();

        
$row = ['f1' => 'v1''f2' => 'v2'];
        
$msgdata = [
            
'{stream}-1' => $row,
            
'{stream}-2' => $row,
        ];

        
/* Append a bit of data and populate STREAM queries */
        
$this->redis->del(array_keys($msgdata));
        foreach (
$msgdata as $key => $message) {
            for (
$r 0$r 2$r++) {
                
$id $this->redis->xAdd($key'*'$message);
                
$qresult[$key][$id] = $message;
            }
            
$qzero[$key] = 0;
            
$qnew[$key] = '$';
            
$keys[] = $key;
        }

        
/* Everything from both streams */
        
$rmsg $this->redis->xRead($qzero);
        
$this->assertEquals($rmsg$qresult);

        
/* Test COUNT option */
        
for ($count 1$count <= 2$count++) {
            
$rmsg $this->redis->xRead($qzero$count);
            foreach (
$keys as $key) {
                
$this->assertEquals(count($rmsg[$key]), $count);
            }
        }

        
/* Should be empty (no new entries) */
        
$this->assertEquals(count($this->redis->xRead($qnew)),0);

        
/* Test against a specific ID */
        
$id $this->redis->xAdd('{stream}-1''*'$row);
        
$new_id $this->redis->xAdd('{stream}-1''*', ['final' => 'row']);
        
$rmsg $this->redis->xRead(['{stream}-1' => $id]);
        
$this->assertEquals(
            
$this->redis->xRead(['{stream}-1' => $id]),
            [
'{stream}-1' => [$new_id => ['final' => 'row']]]
        );

        
/* Emtpy query should fail */
        
$this->assertFalse($this->redis->xRead([]));
    }

    public function 
testXRead() {
        if (!
$this->minVersionCheck("5.0"))
            return 
$this->markTestSkipped();

        foreach (
$this->serializers as $serializer) {
            
$this->redis->setOption(Redis::OPT_SERIALIZER$serializer);
            
$this->doXReadTest();
        }

        
/* Don't need to test BLOCK multiple times */
        
$m1 round(microtime(true)*1000);
        
$this->redis->xRead(['somestream' => '$'], -1100);
        
$m2 round(microtime(true)*1000);
        
$this->assertTrue($m2 $m1 >= 100);
    }

    protected function 
compareStreamIds($redis$control) {
        foreach (
$control as $stream => $ids) {
            
$rcount count($redis[$stream]);
            
$lcount count($control[$stream]);

            
/* We should have the same number of messages */
            
$this->assertEquals($rcount$lcount);

            
/* We should have the exact same IDs */
            
foreach ($ids as $k => $id) {
                
$this->assertTrue(isset($redis[$stream][$id]));
            }
        }
    }

    public function 
testXReadGroup() {
        if (!
$this->minVersionCheck("5.0"))
            return 
$this->markTestSkipped();

        
/* Create some streams and groups */
        
$streams = ['{s}-1''{s}-2'];
        
$groups = ['g1' => 0'g2' => 0];

        
/* I'm not totally sure why Redis behaves this way, but we have to
         * send '>' first and then send ID '0' for subsequent xReadGroup calls
         * or Redis will not return any messages.  This behavior changed from
         * redis 5.0.1 and 5.0.2 but doing it this way works for both versions. */
        
$qcount 0;
        
$query1 = ['{s}-1' => '>''{s}-2' => '>'];
        
$query2 = ['{s}-1' => '0''{s}-2' => '0'];

        
$ids $this->addStreamsAndGroups($streams1$groups);

        
/* Test that we get get the IDs we should */
        
foreach (['g1''g2'] as $group) {
            foreach (
$ids as $stream => $messages) {
                while (
$ids[$stream]) {
                    
/* Read more messages */
                    
$query = !$qcount++ ? $query1 $query2;
                    
$resp $this->redis->xReadGroup($group'consumer'$query);

                    
/* They should match with our local control array */
                    
$this->compareStreamIds($resp$ids);

                    
/* Remove a message from our control *and* XACK it in Redis */
                    
$id array_shift($ids[$stream]);
                    
$this->redis->xAck($stream$group, [$id]);
                }
            }
        }

        
/* Test COUNT option */
        
for ($c 1$c <= 3$c++) {
            
$this->addStreamsAndGroups($streams3$groups);
            
$resp $this->redis->xReadGroup('g1''consumer'$query1$c);

            foreach (
$resp as $stream => $smsg) {
                
$this->assertEquals(count($smsg), $c);
            }
        }

        
/* Test COUNT option with NULL (should be ignored) */
        
$this->addStreamsAndGroups($streams3$groupsNULL);
        
$resp $this->redis->xReadGroup('g1''consumer'$query1NULL);
        foreach (
$resp as $stream => $smsg) {
            
$this->assertEquals(count($smsg), 3);
        }

        
/* Finally test BLOCK with a sloppy timing test */
        
$t1 $this->mstime();
        
$qnew = ['{s}-1' => '>''{s}-2' => '>'];
        
$this->redis->xReadGroup('g1''c1'$qnewNULL100);
        
$t2 $this->mstime();
        
$this->assertTrue($t2 $t1 >= 100);

        
/* Make sure passing NULL to block doesn't block */
        
$t1 $this->mstime();
        
$this->redis->xReadGroup('g1''c1'$qnewNULLNULL);
        
$t2 $this->mstime();
        
$this->assertTrue($t2 $t1 100);

        
/* Make sure passing bad values to BLOCK or COUNT immediately fails */
        
$this->assertFalse(@$this->redis->xReadGroup('g1''c1'$qnew, -1));
        
$this->assertFalse(@$this->redis->xReadGroup('g1''c1'$qnewNULL, -1));
    }

    public function 
testXPending() {
        if (!
$this->minVersionCheck("5.0")) {
            return 
$this->markTestSkipped();
        }

        
$rows 5;
        
$this->addStreamsAndGroups(['s'], $rows, ['group' => 0]);

        
$msg $this->redis->xReadGroup('group''consumer', ['s' => 0]);
        
$ids array_keys($msg['s']);

        for (
$n count($ids); $n >= 0$n--) {
            
$xp $this->redis->xPending('s''group');
            
$this->assertEquals($xp[0], count($ids));

            
/* Verify we're seeing the IDs themselves */
            
for ($idx 1$idx <= 2$idx++) {
                if (
$xp[$idx]) {
                    
$this->assertPatternMatch($xp[$idx], "/^[0-9].*-[0-9].*/");
                }
            }

            if (
$ids) {
                
$id array_shift($ids);
                
$this->redis->xAck('s''group', [$id]);
            }
        }
    }

    public function 
testXDel() {
        if (!
$this->minVersionCheck("5.0"))
            return 
$this->markTestSkipped();

        for (
$n 5$n 0$n--) {
            
$ids $this->addStreamEntries('s'5);
            
$todel array_slice($ids0$n);
            
$this->assertEquals($this->redis->xDel('s'$todel), count($todel));
        }

        
/* Empty array should fail */
        
$this->assertFalse($this->redis->xDel('s', []));
    }

    public function 
testXTrim() {
        if (!
$this->minVersionCheck("5.0"))
            return 
$this->markTestSkipped();

        for (
$maxlen 0$maxlen <= 50$maxlen += 10) {
            
$this->addStreamEntries('stream'100);
            
$trimmed $this->redis->xTrim('stream'$maxlen);
            
$this->assertEquals($trimmed100 $maxlen);
        }

        
/* APPROX trimming isn't easily deterministic, so just make sure we
           can call it with the flag */
        
$this->addStreamEntries('stream'100);
        
$this->assertFalse($this->redis->xTrim('stream'1true) === false);
    }

    
/* XCLAIM is one of the most complicated commands, with a great deal of different options
     * The following test attempts to verify every combination of every possible option. */
    
public function testXClaim() {
        if (!
$this->minVersionCheck("5.0"))
            return 
$this->markTestSkipped();

        foreach ([
0100] as $min_idle_time) {
            foreach ([
falsetrue] as $justid) {
                foreach ([
010] as $retrycount) {
                    
/* We need to test not passing TIME/IDLE as well as passing either */
                    
if ($min_idle_time == 0) {
                        
$topts = [[], ['IDLE'1000000], ['TIME'time() * 1000]];
                    } else {
                        
$topts = [NULL];
                    }

                    foreach (
$topts as $tinfo) {
                        if (
$tinfo) {
                            list(
$ttype$tvalue) = $tinfo;
                        } else {
                            
$ttype NULL$tvalue NULL;
                        }

                        
/* Add some messages and create a group */
                        
$this->addStreamsAndGroups(['s'], 10, ['group1' => 0]);

                        
/* Create a second stream we can FORCE ownership on */
                        
$fids $this->addStreamsAndGroups(['f'], 10, ['group1' => 0]);
                        
$fids $fids['f'];

                        
/* Have consumer 'Mike' read the messages */
                        
$oids $this->redis->xReadGroup('group1''Mike', ['s' => '>']);
                        
$oids array_keys($oids['s']); /* We're only dealing with stream 's' */

                        /* Construct our options array */
                        
$opts = [];
                        if (
$justid$opts[] = 'JUSTID';
                        if (
$retrycount$opts['RETRYCOUNT'] = $retrycount;
                        if (
$tvalue !== NULL$opts[$ttype] = $tvalue;

                        
/* Now have pavlo XCLAIM them */
                        
$cids $this->redis->xClaim('s''group1''Pavlo'$min_idle_time$oids$opts);
                        if (!
$justid$cids array_keys($cids);

                        if (
$min_idle_time == 0) {
                            
$this->assertEquals($cids$oids);

                            
/* Append the FORCE option to our second stream where we have not already
                             * assigned to a PEL group */
                            
$opts[] = 'FORCE';
                            
$freturn $this->redis->xClaim('f''group1''Test'0$fids$opts);
                            if (!
$justid$freturn array_keys($freturn);
                            
$this->assertEquals($freturn$fids);

                            if (
$retrycount || $tvalue !== NULL) {
                                
$pending $this->redis->xPending('s''group1'0'+'1'Pavlo');

                                if (
$retrycount) {
                                    
$this->assertEquals($pending[0][3], $retrycount);
                                }
                                if (
$tvalue !== NULL) {
                                    if (
$ttype == 'IDLE') {
                                        
/* If testing IDLE the value must be >= what we set */
                                        
$this->assertTrue($pending[0][2] >= $tvalue);
                                    } else {
                                        
/* Timing tests are notoriously irritating but I don't see
                                         * how we'll get >= 20,000 ms between XCLAIM and XPENDING no
                                         * matter how slow the machine/VM running the tests is */
                                        
$this->assertTrue($pending[0][2] <= 20000);
                                    }
                                }
                            }
                        } else {
                            
/* We're verifying that we get no messages when we've set 100 seconds
                             * as our idle time, which should match nothing */
                            
$this->assertEquals($cids, []);
                        }
                    }
                }
            }
        }
    }

    public function 
testXInfo()
    {
        if (!
$this->minVersionCheck("5.0")) {
            return 
$this->markTestSkipped();
        }
        
/* Create some streams and groups */
        
$stream 's';
        
$groups = ['g1' => 0'g2' => 0];
        
$this->addStreamsAndGroups([$stream], 1$groups);

        
$info $this->redis->xInfo('GROUPS'$stream);
        
$this->assertTrue(is_array($info));
        
$this->assertEquals(count($info), count($groups));
        foreach (
$info as $group) {
            
$this->assertTrue(array_key_exists('name'$group));
            
$this->assertTrue(array_key_exists($group['name'], $groups));
        }

        
$info $this->redis->xInfo('STREAM'$stream);
        
$this->assertTrue(is_array($info));
        
$this->assertTrue(array_key_exists('groups'$info));
        
$this->assertEquals($info['groups'], count($groups));
        foreach ([
'first-entry''last-entry'] as $key) {
            
$this->assertTrue(array_key_exists($key$info));
            
$this->assertTrue(is_array($info[$key]));
        }

        
/* XINFO STREAM FULL [COUNT N] Requires >= 6.0.0 */
        
if (!$this->minVersionCheck("6.0"))
            return;

        
/* Add some items to the stream so we can test COUNT */
        
for ($i 0$i 5$i++) {
            
$this->redis->xAdd($stream'*', ['foo' => 'bar']);
        }

        
$info $this->redis->xInfo('STREAM'$stream'full');
        
$this->assertTrue(isset($info['groups']));

        for (
$count 1$count 5$count++) {
            
$info $this->redis->xInfo('STREAM'$stream'full'$count);
            
$n = isset($info['entries']) ? count($info['entries']) : 0;
            
$this->assertEquals($n$count);
        }

        
/* Count <= 0 should be ignored */
        
foreach ([-10] as $count) {
            
$info $this->redis->xInfo('STREAM'$stream'full'0);
            
$n = isset($info['entries']) ? count($info['entries']) : 0;
            
$this->assertEquals($n$this->redis->xLen($stream));
        }
    }

    
/* Regression test for issue-1831 (XINFO STREAM on an empty stream) */
    
public function testXInfoEmptyStream() {
        
/* Configure an empty stream */
        
$this->redis->del('s');
        
$this->redis->xAdd('s''*', ['foo' => 'bar']);
        
$this->redis->xTrim('s'0);

        
$arr_info $this->redis->xInfo('STREAM''s');

        
$this->assertTrue(is_array($arr_info));
        
$this->assertEquals(0$arr_info['length']);
        
$this->assertEquals(NULL$arr_info['first-entry']);
        
$this->assertEquals(NULL$arr_info['last-entry']);
    }

    public function 
testInvalidAuthArgs() {
        
$obj_new $this->newInstance();

        
$arr_args = [
            [],
            [
NULLNULL],
            [
'foo''bar''baz'],
            [
'a','b','c','d'],
            [
'a','b','c'],
            [[
'a','b'], 'a'],
            [[
'a','b','c']],
            [[
NULL'pass']],
            [[
NULLNULL]],
        ];

        foreach (
$arr_args as $arr_arg) {
            try {
                if (
is_array($arr_arg)) {
                    @
call_user_func_array([$obj_new'auth'], $arr_arg);
                }
            } catch (
Exception $ex) {
                unset(
$ex); /* Suppress intellisense warning */
            
} catch (ArgumentCountError $ex) {
                unset(
$ex); /* Suppress intellisense warning */
            
}
        }
    }

    public function 
testAcl() {
        if ( ! 
$this->minVersionCheck("6.0"))
            return 
$this->markTestSkipped();

        
/* ACL USERS/SETUSER */
        
$this->assertTrue(in_array('default'$this->redis->acl('USERS')));
        
$this->assertTrue($this->redis->acl('SETUSER''admin''on''>admin''+@all'));
        
$this->assertTrue($this->redis->acl('SETUSER''noperm''on''>noperm''-@all'));
        
$this->assertInArray('default'$this->redis->acl('USERS'));

        
/* Verify ACL GETUSER has the correct hash and is in 'nice' format */
        
$arr_admin $this->redis->acl('GETUSER''admin');
        
$this->assertInArray(hash('sha256''admin'), $arr_admin['passwords']);

        
/* Now nuke our 'admin' user and make sure it went away */
        
$this->assertTrue($this->redis->acl('DELUSER''admin'));
        
$this->assertTrue(!in_array('admin'$this->redis->acl('USERS')));

        
/* Try to log in with a bad username/password */
        
$this->assertThrowsMatch($this->redis,
            function(
$o) { $o->auth(['1337haxx00r''lolwut']); }, '/^WRONGPASS.*$/');

        
/* We attempted a bad login.  We should have an ACL log entry */
        
$arr_log $this->redis->acl('log');
        if (! 
$arr_log || !is_array($arr_log)) {
            
$this->assertTrue(false);
            return;
        }

        
/* Make sure our ACL LOG entries are nice for the user */
        
$arr_entry array_shift($arr_log);
        
$this->assertArrayKey($arr_entry'age-seconds''is_numeric');
        
$this->assertArrayKey($arr_entry'count''is_int');

        
/* ACL CAT */
        
$cats $this->redis->acl('CAT');
        foreach ([
'read''write''slow'] as $cat) {
            
$this->assertInArray($cat$cats);
        }

        
/* ACL CAT <string> */
        
$cats $this->redis->acl('CAT''string');
        foreach ([
'get''set''setnx'] as $cat) {
            
$this->assertInArray($cat$cats);
        }

        
/* ctype_xdigit even if PHP doesn't have it */
        
$ctype_xdigit = function($v) {
            if (
function_exists('ctype_xdigit')) {
                return 
ctype_xdigit($v);
            } else {
                return 
strspn(strtoupper($v), '0123456789ABCDEF') == strlen($v);
            }
        };

        
/* ACL GENPASS/ACL GENPASS <bits> */
        
$this->assertValidate($this->redis->acl('GENPASS'), $ctype_xdigit);
        
$this->assertValidate($this->redis->acl('GENPASS'1024), $ctype_xdigit);

        
/* ACL WHOAMI */
        
$this->assertValidate($this->redis->acl('WHOAMI'), 'strlen');

        
/* Finally make sure AUTH errors throw an exception */
        
$r2 $this->newInstance(true);

        
/* Test NOPERM exception */
        
$this->assertTrue($r2->auth(['noperm''noperm']));
        
$this->assertThrowsMatch($r2, function($r) { $r->set('foo''bar'); }, '/^NOPERM.*$/');
    }

    
/* If we detect a unix socket make sure we can connect to it in a variety of ways */
    
public function testUnixSocket() {
        if ( ! 
file_exists("/tmp/redis.sock")) {
            return 
$this->markTestSkipped();
        }

        
$arr_sock_tests = [
            [
"/tmp/redis.sock"],
            [
"/tmp/redis.sock"null],
            [
"/tmp/redis.sock"0],
            [
"/tmp/redis.sock", -1],
        ];

        try {
            foreach (
$arr_sock_tests as $arr_args) {
                
$obj_r = new Redis();

                if (
count($arr_args) == 2) {
                    @
$obj_r->connect($arr_args[0], $arr_args[1]);
                } else {
                    @
$obj_r->connect($arr_args[0]);
                }
                if (
$this->getAuth()) {
                    
$this->assertTrue($obj_r->auth($this->getAuth()));
                }
                
$this->assertTrue($obj_r->ping());
            }
        } catch (
Exception $ex) {
            
$this->assertTrue(false);
        }
    }

    
/* Test high ports if we detect Redis running there */
    
public function testHighPorts() {
        
$arr_ports = [327673276832769];
        
$arr_test_ports = [];

        foreach (
$arr_ports as $port) {
            if (
is_resource(@fsockopen('localhost'$port))) {
                
$arr_test_ports[] = $port;
            }
        }

        if ( ! 
$arr_test_ports) {
            return 
$this->markTestSkipped();
        }

        foreach (
$arr_test_ports as $port) {
            
$obj_r = new Redis();
            try {
                @
$obj_r->connect('localhost'$port);
                if (
$this->getAuth()) {
                    
$this->assertTrue($obj_r->auth($this->getAuth()));
                }
                
$this->assertTrue($obj_r->ping());
            } catch(
Exception $ex) {
                
$this->assertTrue(false);
            }
        }
    }

    public function 
testSession_savedToRedis()
    {
        
$this->setSessionHandler();

        
$sessionId $this->generateSessionId();
        
$sessionSuccessful $this->startSessionProcess($sessionId0false);

        
$this->assertTrue($this->redis->exists($this->sessionPrefix $sessionId));
        
$this->assertTrue($sessionSuccessful);
    }

    public function 
testSession_lockKeyCorrect()
    {
        
$this->setSessionHandler();
        
$sessionId $this->generateSessionId();

        
$this->startSessionProcess($sessionId5true);

        
$maxwait = (ini_get('redis.session.lock_wait_time') *
                    
ini_get('redis.session.lock_retries') /
                    
1000000.00);

        
$exist $this->waitForSessionLockKey($sessionId$maxwait 1);
        
$this->assertTrue($exist);
    }

    public function 
testSession_lockingDisabledByDefault()
    {
        
$this->setSessionHandler();
        
$sessionId $this->generateSessionId();
        
$this->startSessionProcess($sessionId5true300false);
        
usleep(100000);

        
$start microtime(true);
        
$sessionSuccessful $sessionSuccessful $this->startSessionProcess($sessionId0false300false);
        
$end microtime(true);
        
$elapsedTime $end $start;

        
$this->assertFalse($this->redis->exists($this->sessionPrefix $sessionId '_LOCK'));
        
$this->assertTrue($elapsedTime 1);
        
$this->assertTrue($sessionSuccessful);
    }

    public function 
testSession_lockReleasedOnClose()
    {
        
$this->setSessionHandler();
        
$sessionId $this->generateSessionId();
        
$this->startSessionProcess($sessionId1true);

        
/* Wait for a key to actually exist */
        
if ( ! $this->waitForSessionLockKey($sessionId1)) {
            
$this->assertFalse(true);
            return;
        }

        
/* Wait long enough for our background process to exit */
        
usleep(1100000);

        
/* Key should have been deleted */
        
$this->assertFalse($this->redis->exists($this->sessionPrefix $sessionId '_LOCK'));
    }

    public function 
testSession_lock_ttlMaxExecutionTime()
    {
        
$this->setSessionHandler();
        
$sessionId $this->generateSessionId();
        
$this->startSessionProcess($sessionId10true2);
        
usleep(100000);

        
$start microtime(true);
        
$sessionSuccessful $this->startSessionProcess($sessionId0false);
        
$end microtime(true);
        
$elapsedTime $end $start;

        
$this->assertTrue($elapsedTime 3);
        
$this->assertTrue($sessionSuccessful);
    }

    public function 
testSession_lock_ttlLockExpire()
    {
        
$this->setSessionHandler();
        
$sessionId $this->generateSessionId();
        
$this->startSessionProcess($sessionId10true300truenull, -12);
        
usleep(100000);

        
$start microtime(true);
        
$sessionSuccessful $this->startSessionProcess($sessionId0false);
        
$end microtime(true);
        
$elapsedTime $end $start;

        
$this->assertTrue($elapsedTime 3);
        
$this->assertTrue($sessionSuccessful);
    }

    public function 
testSession_lockHoldCheckBeforeWrite_otherProcessHasLock()
    {
        
$this->setSessionHandler();
        
$sessionId $this->generateSessionId();
        
$this->startSessionProcess($sessionId2true300truenull, -11'firstProcess');
        
usleep(1500000); // 1.5 sec
        
$writeSuccessful $this->startSessionProcess($sessionId0false300truenull, -110'secondProcess');
        
sleep(1);

        
$this->assertTrue($writeSuccessful);
        
$this->assertEquals('secondProcess'$this->getSessionData($sessionId));
    }

    public function 
testSession_lockHoldCheckBeforeWrite_nobodyHasLock()
    {
        
$this->setSessionHandler();
        
$sessionId $this->generateSessionId();
        
$writeSuccessful $this->startSessionProcess($sessionId2false300truenull, -11'firstProcess');

        
$this->assertFalse($writeSuccessful);
        
$this->assertTrue('firstProcess' !== $this->getSessionData($sessionId));
    }

    public function 
testSession_correctLockRetryCount() {
        
$this->setSessionHandler();
        
$sessionId $this->generateSessionId();

        
/* Start another process and wait until it has the lock */
        
$this->startSessionProcess($sessionId10true);
        if ( ! 
$this->waitForSessionLockKey($sessionId2)) {
            
$this->assertTrue(false);
            return;
        }

        
$t1 microtime(true);
        
$ok $this->startSessionProcess($sessionId0false10true10000010);
        if ( ! 
$this->assertFalse($ok)) return;
        
$t2 microtime(true);

        
$this->assertTrue($t2 $t1 >= && $t2 $t1 <= 3);
    }

    public function 
testSession_defaultLockRetryCount()
    {
        
$this->setSessionHandler();
        
$sessionId $this->generateSessionId();
        
$this->startSessionProcess($sessionId10true);

        
$keyname $this->sessionPrefix $sessionId '_LOCK';
        
$begin microtime(true);

        if ( ! 
$this->waitForSessionLockKey($sessionId3)) {
            
$this->assertTrue(false);
            return;
        }

        
$start microtime(true);
        
$sessionSuccessful $this->startSessionProcess($sessionId0false10true2000000);
        
$end microtime(true);
        
$elapsedTime $end $start;

        
$this->assertTrue($elapsedTime && $elapsedTime 3);
        
$this->assertFalse($sessionSuccessful);
    }

    public function 
testSession_noUnlockOfOtherProcess()
    {
        
$this->setSessionHandler();
        
$sessionId $this->generateSessionId();

        
$t1 microtime(true);

        
/* 1.  Start a background process, and wait until we are certain
         *     the lock was attained. */
        
$nsec 3;
        
$this->startSessionProcess($sessionId$nsectrue$nsec);
        if ( ! 
$this->waitForSessionLockKey($sessionId1)) {
            
$this->assertFalse(true);
            return;
        }

        
/* 2.  Attempt to lock the same session.  This should force us to
         *     wait until the first lock is released. */
        
$t2 microtime(true);
        
$ok $this->startSessionProcess($sessionId0false);
        
$t3 microtime(true);

        
/* 3.  Verify that we did in fact have to wait for this lock */
        
$this->assertTrue($ok);
        
$this->assertTrue($t3 $t2 >= $nsec - ($t2 $t1));
    }

    public function 
testSession_lockWaitTime()
    {
        
$this->setSessionHandler();
        
$sessionId $this->generateSessionId();
        
$this->startSessionProcess($sessionId1true300);
        
usleep(100000);

        
$start microtime(true);
        
$sessionSuccessful $this->startSessionProcess($sessionId0false300true3000000);
        
$end microtime(true);
        
$elapsedTime $end $start;

        
$this->assertTrue($elapsedTime 2.5);
        
$this->assertTrue($elapsedTime 3.5);
        
$this->assertTrue($sessionSuccessful);
    }

    public function 
testMultipleConnect() {
        
$host $this->redis->GetHost();
        
$port $this->redis->GetPort();

        for(
$i 0$i 5$i++) {
            
$this->redis->connect($host$port);
            if (
$this->getAuth()) {
                
$this->assertTrue($this->redis->auth($this->getAuth()));
            }
            
$this->assertTrue($this->redis->ping());
        }
    }

    public function 
testConnectException() {
        
$host 'github.com';
        if (
gethostbyname($host) === $host) {
            return 
$this->markTestSkipped('online test');
        }
        
$redis = new Redis();
        try {
            
$redis->connect($host63790.01);
        }  catch (
Exception $e) {
            
$this->assertTrue(strpos($e"timed out") !== false);
        }
    }

    public function 
testTlsConnect()
    {
        if ((
$fp = @fsockopen($this->getHost(), 6378)) == NULL)
            return 
$this->markTestSkipped();

        
fclose($fp);

        foreach ([
'localhost' => true'127.0.0.1' => false] as $host => $verify) {
            
$redis = new Redis();
            
$this->assertTrue($redis->connect('tls://' $host63780null00, [
                
'stream' => ['verify_peer_name' => $verify'verify_peer' => false]
            ]));
        }
    }

    public  function 
testSession_regenerateSessionId_noLock_noDestroy() {
        
$this->setSessionHandler();
        
$sessionId $this->generateSessionId();
        
$this->startSessionProcess($sessionId0false300truenull, -11'bar');

        
$newSessionId $this->regenerateSessionId($sessionId);

        
$this->assertTrue($newSessionId !== $sessionId);
        
$this->assertEquals('bar'$this->getSessionData($newSessionId));
    }

    public  function 
testSession_regenerateSessionId_noLock_withDestroy() {
        
$this->setSessionHandler();
        
$sessionId $this->generateSessionId();
        
$this->startSessionProcess($sessionId0false300truenull, -11'bar');

        
$newSessionId $this->regenerateSessionId($sessionIdfalsetrue);

        
$this->assertTrue($newSessionId !== $sessionId);
        
$this->assertEquals('bar'$this->getSessionData($newSessionId));
    }

    public  function 
testSession_regenerateSessionId_withLock_noDestroy() {
        
$this->setSessionHandler();
        
$sessionId $this->generateSessionId();
        
$this->startSessionProcess($sessionId0false300truenull, -11'bar');

        
$newSessionId $this->regenerateSessionId($sessionIdtrue);

        
$this->assertTrue($newSessionId !== $sessionId);
        
$this->assertEquals('bar'$this->getSessionData($newSessionId));
    }

    public  function 
testSession_regenerateSessionId_withLock_withDestroy() {
        
$this->setSessionHandler();
        
$sessionId $this->generateSessionId();
        
$this->startSessionProcess($sessionId0false300truenull, -11'bar');

        
$newSessionId $this->regenerateSessionId($sessionIdtruetrue);

        
$this->assertTrue($newSessionId !== $sessionId);
        
$this->assertEquals('bar'$this->getSessionData($newSessionId));
    }

    public  function 
testSession_regenerateSessionId_noLock_noDestroy_withProxy() {
        if (!
interface_exists('SessionHandlerInterface')) {
            
$this->markTestSkipped('session handler interface not available in PHP < 5.4');
        }

        
$this->setSessionHandler();
        
$sessionId $this->generateSessionId();
        
$this->startSessionProcess($sessionId0false300truenull, -11'bar');

        
$newSessionId $this->regenerateSessionId($sessionIdfalsefalsetrue);

        
$this->assertTrue($newSessionId !== $sessionId);
        
$this->assertEquals('bar'$this->getSessionData($newSessionId));
    }

    public  function 
testSession_regenerateSessionId_noLock_withDestroy_withProxy() {
        if (!
interface_exists('SessionHandlerInterface')) {
            
$this->markTestSkipped('session handler interface not available in PHP < 5.4');
        }

        
$this->setSessionHandler();
        
$sessionId $this->generateSessionId();
        
$this->startSessionProcess($sessionId0false300truenull, -11'bar');

        
$newSessionId $this->regenerateSessionId($sessionIdfalsetruetrue);

        
$this->assertTrue($newSessionId !== $sessionId);
        
$this->assertEquals('bar'$this->getSessionData($newSessionId));
    }

    public  function 
testSession_regenerateSessionId_withLock_noDestroy_withProxy() {
        if (!
interface_exists('SessionHandlerInterface')) {
            
$this->markTestSkipped('session handler interface not available in PHP < 5.4');
        }

        
$this->setSessionHandler();
        
$sessionId $this->generateSessionId();
        
$this->startSessionProcess($sessionId0false300truenull, -11'bar');

        
$newSessionId $this->regenerateSessionId($sessionIdtruefalsetrue);

        
$this->assertTrue($newSessionId !== $sessionId);
        
$this->assertEquals('bar'$this->getSessionData($newSessionId));
    }

    public  function 
testSession_regenerateSessionId_withLock_withDestroy_withProxy() {
        if (!
interface_exists('SessionHandlerInterface')) {
            
$this->markTestSkipped('session handler interface not available in PHP < 5.4');
        }

        
$this->setSessionHandler();
        
$sessionId $this->generateSessionId();
        
$this->startSessionProcess($sessionId0false300truenull, -11'bar');

        
$newSessionId $this->regenerateSessionId($sessionIdtruetruetrue);

        
$this->assertTrue($newSessionId !== $sessionId);
        
$this->assertEquals('bar'$this->getSessionData($newSessionId));
    }

    public function 
testSession_ttl_equalsToSessionLifetime()
    {
        
$sessionId $this->generateSessionId();
        
$this->startSessionProcess($sessionId0false300truenull, -10'test'600);
        
$ttl $this->redis->ttl($this->sessionPrefix $sessionId);

        
$this->assertEquals(600$ttl);
    }

    public function 
testSession_ttl_resetOnWrite()
    {
        
$sessionId $this->generateSessionId();
        
$this->startSessionProcess($sessionId0false300truenull, -10'test'600);
        
$this->redis->expire($this->sessionPrefix $sessionId9999);
        
$this->startSessionProcess($sessionId0false300truenull, -10'test'600);
        
$ttl $this->redis->ttl($this->sessionPrefix $sessionId);

        
$this->assertEquals(600$ttl);
    }

    public function 
testSession_ttl_resetOnRead()
    {
        
$sessionId $this->generateSessionId();
        
$this->startSessionProcess($sessionId0false300truenull, -10'test'600);
        
$this->redis->expire($this->sessionPrefix $sessionId9999);
        
$this->getSessionData($sessionId600);
        
$ttl $this->redis->ttl($this->sessionPrefix $sessionId);

        
$this->assertEquals(600$ttl);
    }

    private function 
setSessionHandler()
    {
        
$host $this->getHost() ?: 'localhost';

        @
ini_set('session.save_handler''redis');
        @
ini_set('session.save_path''tcp://' $host ':6379');
    }

    
/**
     * @return string
     */
    
private function generateSessionId()
    {
        if (
function_exists('session_create_id')) {
            return 
session_create_id();
        } else if (
function_exists('random_bytes')) {
            return 
bin2hex(random_bytes(8));
        } else if (
function_exists('openssl_random_pseudo_bytes')) {
            return 
bin2hex(openssl_random_pseudo_bytes(8));
        } else {
            return 
uniqid();
        }
    }

    
/**
     * @param string $sessionId
     * @param int    $sleepTime
     * @param bool   $background
     * @param int    $maxExecutionTime
     * @param bool   $locking_enabled
     * @param int    $lock_wait_time
     * @param int    $lock_retries
     * @param int    $lock_expires
     * @param string $sessionData
     *
     * @param int    $sessionLifetime
     *
     * @return bool
     * @throws Exception
     */
    
private function startSessionProcess($sessionId$sleepTime$background$maxExecutionTime 300,
                                         
$locking_enabled true$lock_wait_time null$lock_retries = -1,
                                         
$lock_expires 0$sessionData ''$sessionLifetime 1440)
    {
        if (
substr(php_uname(), 07) == "Windows"){
            
$this->markTestSkipped();
            return 
true;
        } else {
            
$commandParameters = [
                
$this->getFullHostPath(), $this->sessionSaveHandler$sessionId,
                
$sleepTime$maxExecutionTime$lock_retries$lock_expires,
                
$sessionData$sessionLifetime
            
];

            if (
$locking_enabled) {
                
$commandParameters[] = '1';

                if (
$lock_wait_time != null) {
                    
$commandParameters[] = $lock_wait_time;
                }
            }
            
$commandParameters array_map('escapeshellarg'$commandParameters);

            
$command self::getPhpCommand('startSession.php') . implode(' '$commandParameters);
            
$command .= $background ' 2>/dev/null > /dev/null &' ' 2>&1';
            
exec($command$output);
            return (
$background || (count($output) == && $output[0] == 'SUCCESS')) ? true false;
        }
    }

    
/**
     * @param string $session_id
     * @param string $max_wait_sec
     *
     * Sometimes we want to block until a session lock has been detected
     * This is better and faster than arbitrarily sleeping.  If we don't
     * detect the session key within the specified maximum number of
     * seconds, the function returns failure.
     *
     * @return bool
     */
    
private function waitForSessionLockKey($session_id$max_wait_sec) {
        
$now microtime(true);
        
$key $this->sessionPrefix $session_id '_LOCK';

        do {
            
usleep(10000);
            
$exists $this->redis->exists($key);
        } while (!
$exists && microtime(true) <= $now $max_wait_sec);

        return 
$exists || $this->redis->exists($key);
    }

    
/**
     * @param string $str_search pattern to look for in ps
     * @param int    $timeout    Maximum amount of time to wait
     *
     * Small helper function to wait until we no longer detect a running process.
     * This is an attempt to fix timing related false failures on session tests
     * when running in CI.
     */
    
function waitForProcess($str_search$timeout 0.0) {
        
$st microtime(true);

        do {
            
$str_procs shell_exec("ps aux|grep $str_search|grep -v grep");
            
$arr_procs array_filter(explode("\n"$str_procs));
            if (
count($arr_procs) == 0)
                return 
true;

            
usleep(10000);
            
$elapsed microtime(true) - $st;
        } while (
$timeout || $elapsed $timeout);

        return 
false;
    }

    
/**
     * @param string $sessionId
     * @param int    $sessionLifetime
     *
     * @return string
     */
    
private function getSessionData($sessionId$sessionLifetime 1440)
    {
        
$command self::getPhpCommand('getSessionData.php') . escapeshellarg($this->getFullHostPath()) . ' ' $this->sessionSaveHandler ' ' escapeshellarg($sessionId) . ' ' escapeshellarg($sessionLifetime);
        
exec($command$output);

        return 
$output[0];
    }

    
/**
     * @param string $sessionId
     * @param bool   $locking
     * @param bool   $destroyPrevious
     * @param bool   $sessionProxy
     *
     * @return string
     */
    
private function regenerateSessionId($sessionId$locking false$destroyPrevious false$sessionProxy false)
    {
    
$args array_map('escapeshellarg', [$sessionId$locking$destroyPrevious$sessionProxy]);

        
$command self::getPhpCommand('regenerateSessionId.php') . escapeshellarg($this->getFullHostPath()) . ' ' $this->sessionSaveHandler ' ' implode(' '$args);

        
exec($command$output);

        return 
$output[0];
    }

    
/**
     * Return command to launch PHP with built extension enabled
     * taking care of environment (TEST_PHP_EXECUTABLE and TEST_PHP_ARGS)
     *
     * @param string $script
     *
     * @return string
     */
    
private static function getPhpCommand($script)
    {
        static 
$cmd NULL;

        if (!
$cmd) {
            
$cmd  = (getenv('TEST_PHP_EXECUTABLE') ?: PHP_BINARY);

            if (
$test_args getenv('TEST_PHP_ARGS')) {
                
$cmd .= ' ';
                
$cmd .= $test_args;
            } else {
                
/* Only append specific extension directives if PHP hasn't been compiled with what we need statically */
                
$modules   shell_exec("$cmd --no-php-ini -m");

                
/* Determine if we need to specifically add extensions */
                
$arr_extensions = [];

                
/* If any are needed add them to the command */
                
if ($arr_extensions) {
                    
$cmd .= ' --no-php-ini';
                    foreach (
$arr_extensions as $str_extension) {
                        
/* We want to use the locally built redis extension */
                        
if ($str_extension == 'redis') {
                            
$str_extension dirname(__DIR__) . '/modules/redis';
                        }

                        
$cmd .= " --define extension=$str_extension.so";
                    }
                }
            }
        }

        return 
$cmd ' ' __DIR__ '/' $script ' ';
    }
}
?>

:: Command execute ::

Enter:
 
Select:
 

:: Search ::
  - regexp 

:: Upload ::
 
[ Read-Only ]

:: Make Dir ::
 
[ Read-Only ]
:: Make File ::
 
[ Read-Only ]

:: Go Dir ::
 
:: Go File ::
 

--[ c99shell v.2.1 [PHP 7 Update] [1.12.2019] maintained by KaizenLouie and updated by cermmik | C99Shell Github (MySQL update) | Generation time: 0.0274 ]--